Commit b50b30c15ea for php.net
commit b50b30c15ea0ba94ab7217dee8e362039600efae
Author: Máté Kocsis <kocsismate@woohoolabs.com>
Date: Fri Jun 12 22:03:27 2026 +0200
standard: Fail unserialization when the `C` format is used for classes that are not `Serializable` (#22058)
Fixes https://github.com/php/php-src/issues/22046
diff --git a/NEWS b/NEWS
index c6fa558db81..ac338190083 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,9 @@ PHP NEWS
. Deprecate specifying a nullable return type for __debugInfo(). (timwolla)
. Fixed bug GH-22142 (Assertion failure in zendi_try_get_long() on IS_UNDEF).
(David Carlier)
+ . Fixed bug GH-22046 (The unserialize function can lead to segfault when
+ non-Serializable internal classes are serialized back with the C format).
+ (kocsismate)
- BCMath:
. Added NUL-byte validation to BCMath functions. (jorgsowa)
diff --git a/ext/standard/tests/serialize/serialization_objects_009.phpt b/ext/standard/tests/serialize/serialization_objects_009.phpt
index 9485f3ef806..95b85ccd80f 100644
--- a/ext/standard/tests/serialize/serialization_objects_009.phpt
+++ b/ext/standard/tests/serialize/serialization_objects_009.phpt
@@ -8,17 +8,16 @@
$b = unserialize($ser);
var_dump($a, $b);
-
echo "Done";
?>
--EXPECTF--
-Warning: Class __PHP_Incomplete_Class has no unserializer in %sserialization_objects_009.php on line %d
+Warning: Class __PHP_Incomplete_Class has no unserializer in %s on line %d
+
+Warning: unserialize(): Error at offset 11 of 18 bytes in %s on line %d
+
+Warning: Class C has no unserializer in %s on line %d
-Warning: Class C has no unserializer in %sserialization_objects_009.php on line %d
-object(__PHP_Incomplete_Class)#%d (1) {
- ["__PHP_Incomplete_Class_Name"]=>
- string(1) "C"
-}
-object(C)#%d (0) {
-}
+Warning: unserialize(): Error at offset 11 of 18 bytes in %s on line %d
+bool(false)
+bool(false)
Done
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index d5019d94dc0..484cb5aa8fc 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -770,7 +770,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce)
if (ce->unserialize == NULL) {
zend_error(E_WARNING, "Class %s has no unserializer", ZSTR_VAL(ce->name));
- object_init_ex(rval, ce);
+ return 0;
} else if (ce->unserialize(rval, ce, (const unsigned char*)*p, datalen, (zend_unserialize_data *)var_hash) != SUCCESS) {
return 0;
}
diff --git a/ext/uri/tests/gh22046.phpt b/ext/uri/tests/gh22046.phpt
new file mode 100644
index 00000000000..af297905aa3
--- /dev/null
+++ b/ext/uri/tests/gh22046.phpt
@@ -0,0 +1,19 @@
+--TEST--
+GH-22046: The unserialize function can lead to segfault when internal classes are serialized back with the unsupported C format
+--FILE--
+<?php
+
+$payload = 'C:14:"Uri\WhatWg\Url":0:{}';
+unserialize($payload);
+
+$payload = 'C:15:"Uri\Rfc3986\Uri":0:{}';
+unserialize($payload);
+?>
+--EXPECTF--
+Warning: Class Uri\WhatWg\Url has no unserializer in %s on line %d
+
+Warning: unserialize(): Error at offset 25 of 26 bytes in %s on line %d
+
+Warning: Class Uri\Rfc3986\Uri has no unserializer in %s on line %d
+
+Warning: unserialize(): Error at offset 26 of 27 bytes in %s on line %d