diff --git a/NEWS b/NEWS index d499246c542a3..94d1193e0a39e 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,8 @@ PHP NEWS (cmb) . Fixed bug GH-9285 (Traits cannot be used in readonly classes). (kocsismate) + . Fixed bug GH-9186 (@strict-properties can be bypassed using + unserialization). (kocsismate) - Date: . Fixed bug GH-9431 (DateTime::getLastErrors() not returning false when no diff --git a/Zend/tests/gc_043.phpt b/Zend/tests/gc_043.phpt index 06b64de39acf9..37906a025145f 100644 --- a/Zend/tests/gc_043.phpt +++ b/Zend/tests/gc_043.phpt @@ -8,7 +8,8 @@ STR; var_dump(unserialize($s)); gc_collect_cycles(); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Creation of dynamic property RegexIterator::$5 is deprecated in %s on line %d object(stdClass)#1 (2) { ["5"]=> object(SplStack)#2 (2) { diff --git a/Zend/tests/readonly_classes/readonly_class_unserialize_error.phpt b/Zend/tests/readonly_classes/readonly_class_unserialize_error.phpt new file mode 100644 index 0000000000000..e0672e4d97f80 --- /dev/null +++ b/Zend/tests/readonly_classes/readonly_class_unserialize_error.phpt @@ -0,0 +1,16 @@ +--TEST-- +Fix GH-9186 Readonly classes can have dynamic properties created by unserialize() +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +Cannot create dynamic property C::$x diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 56db031102095..54bcad825a501 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -1628,6 +1628,15 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zend_hash_update(object->properties, key, &tmp); } } else { + if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", + ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + return; + } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", + ZSTR_VAL(object->ce->name), property_info != ZEND_WRONG_PROPERTY_INFO ? zend_get_unmangled_property_name(key): ""); + } + if (!object->properties) { rebuild_object_properties(object); } @@ -1635,6 +1644,14 @@ ZEND_API void object_properties_load(zend_object *object, HashTable *properties) zval_add_ref(prop); } } else { + if (UNEXPECTED(object->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$" ZEND_LONG_FMT, ZSTR_VAL(object->ce->name), h); + return; + } else if (!(object->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + zend_error(E_DEPRECATED, "Creation of dynamic property %s::$" ZEND_LONG_FMT " is deprecated", + ZSTR_VAL(object->ce->name), h); + } + if (!object->properties) { rebuild_object_properties(object); } diff --git a/ext/gmp/tests/gmp_dynamic_property.phpt b/ext/gmp/tests/gmp_dynamic_property.phpt new file mode 100644 index 0000000000000..547fe51a7f6a3 --- /dev/null +++ b/ext/gmp/tests/gmp_dynamic_property.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-9186 Dynamic property unserialization should trigger a deprecated notice +--EXTENSIONS-- +gmp +--FILE-- +{1} = 123; + +$serialized = serialize($g); +var_dump(unserialize($serialized)); + +?> +--EXPECTF-- +Deprecated: Creation of dynamic property GMP::$1 is deprecated in %s on line %d + +Deprecated: Creation of dynamic property GMP::$1 is deprecated in %s on line %d +object(GMP)#%d (%d) { + [1]=> + int(123) + ["num"]=> + string(1) "0" +} diff --git a/ext/gmp/tests/serialize.phpt b/ext/gmp/tests/serialize.phpt index 65c2f34e13d17..84877459a9bc0 100644 --- a/ext/gmp/tests/serialize.phpt +++ b/ext/gmp/tests/serialize.phpt @@ -54,6 +54,8 @@ object(GMP)#%d (1) { Deprecated: Creation of dynamic property GMP::$foo is deprecated in %s on line %d string(56) "O:3:"GMP":2:{i:0;s:1:"d";i:1;a:1:{s:3:"foo";s:3:"bar";}}" + +Deprecated: Creation of dynamic property GMP::$foo is deprecated in %s on line %d object(GMP)#%d (2) { ["foo"]=> string(3) "bar" diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c index 266ec5ea11c24..a2aed76a39d76 100644 --- a/ext/random/engine_mt19937.c +++ b/ext/random/engine_mt19937.c @@ -365,6 +365,10 @@ PHP_METHOD(Random_Engine_Mt19937, __unserialize) RETURN_THROWS(); } object_properties_load(&engine->std, Z_ARRVAL_P(t)); + if (EG(exception)) { + zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name)); + RETURN_THROWS(); + } /* state */ t = zend_hash_index_find(d, 1); diff --git a/ext/random/randomizer.c b/ext/random/randomizer.c index c94c9449db93d..f26231b997722 100644 --- a/ext/random/randomizer.c +++ b/ext/random/randomizer.c @@ -90,7 +90,7 @@ PHP_METHOD(Random_Randomizer, __construct) /* {{{ Generate positive random number */ PHP_METHOD(Random_Randomizer, nextInt) -{ +{ php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS); uint64_t result; @@ -104,7 +104,7 @@ PHP_METHOD(Random_Randomizer, nextInt) zend_throw_exception(random_ce_Random_RandomException, "Generated value exceeds size of int", 0); RETURN_THROWS(); } - + RETURN_LONG((zend_long) (result >> 1)); } /* }}} */ @@ -278,6 +278,10 @@ PHP_METHOD(Random_Randomizer, __unserialize) RETURN_THROWS(); } object_properties_load(&randomizer->std, Z_ARRVAL_P(members_zv)); + if (EG(exception)) { + zend_throw_exception(NULL, "Invalid serialization data for Random\\Randomizer object", 0); + RETURN_THROWS(); + } zengine = zend_read_property(randomizer->std.ce, &randomizer->std, "engine", strlen("engine"), 1, NULL); if (Z_TYPE_P(zengine) != IS_OBJECT || !instanceof_function(Z_OBJCE_P(zengine), random_ce_Random_Engine)) { diff --git a/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt new file mode 100644 index 0000000000000..fe1acb2f74be4 --- /dev/null +++ b/ext/random/tests/03_randomizer/gh_9186_unserialize.phpt @@ -0,0 +1,14 @@ +--TEST-- +Fix GH-9186 @strict-properties can be bypassed using unserialization +--FILE-- +getMessage() . "\n"; +} + +?> +--EXPECT-- +Invalid serialization data for Random\Randomizer object diff --git a/ext/session/tests/003.phpt b/ext/session/tests/003.phpt index c0628e28abdd9..ab1739edf6b76 100644 --- a/ext/session/tests/003.phpt +++ b/ext/session/tests/003.phpt @@ -16,6 +16,7 @@ error_reporting(E_ALL); class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/session/tests/004.phpt b/ext/session/tests/004.phpt index 0901286001808..d6b53072c1633 100644 --- a/ext/session/tests/004.phpt +++ b/ext/session/tests/004.phpt @@ -52,6 +52,7 @@ $hnd = new handler; class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/session/tests/005.phpt b/ext/session/tests/005.phpt index 6c7f246a3630a..c4083557866e3 100644 --- a/ext/session/tests/005.phpt +++ b/ext/session/tests/005.phpt @@ -53,6 +53,7 @@ $hnd = new handler; class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/session/tests/023.phpt b/ext/session/tests/023.phpt index 78f6e41c7e790..092f8db64b790 100644 --- a/ext/session/tests/023.phpt +++ b/ext/session/tests/023.phpt @@ -16,6 +16,7 @@ error_reporting(E_ALL); class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/session/tests/024.phpt b/ext/session/tests/024.phpt index b0f819b9d0d2f..c307d79a6873b 100644 --- a/ext/session/tests/024.phpt +++ b/ext/session/tests/024.phpt @@ -53,6 +53,7 @@ $hnd = new handler; class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/session/tests/025.phpt b/ext/session/tests/025.phpt index b18715af36651..5f9a5ad591c7d 100644 --- a/ext/session/tests/025.phpt +++ b/ext/session/tests/025.phpt @@ -54,6 +54,7 @@ $hnd = new handler; class foo { public $bar = "ok"; + public $yes; function method() { $this->yes++; } } diff --git a/ext/soap/tests/bug70388.phpt b/ext/soap/tests/bug70388.phpt index 87259bf45d50a..8f3e8be766ea1 100644 --- a/ext/soap/tests/bug70388.phpt +++ b/ext/soap/tests/bug70388.phpt @@ -4,6 +4,8 @@ Bug #70388 (SOAP serialize_function_call() type confusion / RCE) soap --FILE-- std, Z_ARRVAL_P(members_zv)); + if (EG(exception)) { + RETURN_THROWS(); + } if (iterator_class_zv && Z_TYPE_P(iterator_class_zv) == IS_STRING) { zend_class_entry *ce = zend_lookup_class(Z_STR_P(iterator_class_zv)); diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index fd18c3d86c378..a00bb8965af65 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -102,13 +102,18 @@ static void spl_fixedarray_init_elems(spl_fixedarray *array, zend_long from, zen } } +static void spl_fixedarray_init_non_empty_struct(spl_fixedarray *array, zend_long size) +{ + array->size = 0; /* reset size in case ecalloc() fails */ + array->elements = size ? safe_emalloc(size, sizeof(zval), 0) : NULL; + array->size = size; + array->should_rebuild_properties = true; +} + static void spl_fixedarray_init(spl_fixedarray *array, zend_long size) { if (size > 0) { - array->size = 0; /* reset size in case ecalloc() fails */ - array->elements = safe_emalloc(size, sizeof(zval), 0); - array->size = size; - array->should_rebuild_properties = true; + spl_fixedarray_init_non_empty_struct(array, size); spl_fixedarray_init_elems(array, 0, size); } else { spl_fixedarray_default_ctor(array); @@ -582,6 +587,78 @@ PHP_METHOD(SplFixedArray, __wakeup) } } +PHP_METHOD(SplFixedArray, __serialize) +{ + spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS); + zval *current; + zend_string *key; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + uint32_t property_num = zend_hash_num_elements(intern->std.properties); + array_init_size(return_value, intern->array.size + property_num); + + /* elements */ + for (zend_long i = 0; i < intern->array.size; i++) { + current = &intern->array.elements[i]; + zend_hash_next_index_insert(Z_ARRVAL_P(return_value), current); + Z_TRY_ADDREF_P(current); + } + + /* members */ + ZEND_HASH_FOREACH_STR_KEY_VAL(intern->std.properties, key, current) { + zend_hash_add(Z_ARRVAL_P(return_value), key, current); + Z_TRY_ADDREF_P(current); + } ZEND_HASH_FOREACH_END(); +} + +PHP_METHOD(SplFixedArray, __unserialize) +{ + spl_fixedarray_object *intern = Z_SPLFIXEDARRAY_P(ZEND_THIS); + HashTable *data; + zval members_zv, *elem; + zend_string *key; + zend_long size; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "h", &data) == FAILURE) { + RETURN_THROWS(); + } + + if (intern->array.size == 0) { + size = zend_hash_num_elements(data); + spl_fixedarray_init_non_empty_struct(&intern->array, size); + if (!size) { + return; + } + array_init(&members_zv); + + intern->array.size = 0; + ZEND_HASH_FOREACH_STR_KEY_VAL(data, key, elem) { + if (key == NULL) { + ZVAL_COPY(&intern->array.elements[intern->array.size], elem); + intern->array.size++; + } else { + Z_TRY_ADDREF_P(elem); + zend_hash_add(Z_ARRVAL(members_zv), key, elem); + } + } ZEND_HASH_FOREACH_END(); + + if (intern->array.size != size) { + if (intern->array.size) { + intern->array.elements = erealloc(intern->array.elements, sizeof(zval) * intern->array.size); + } else { + efree(intern->array.elements); + intern->array.elements = NULL; + } + } + + object_properties_load(&intern->std, Z_ARRVAL(members_zv)); + zval_ptr_dtor(&members_zv); + } +} + PHP_METHOD(SplFixedArray, count) { zval *object = ZEND_THIS; diff --git a/ext/spl/spl_fixedarray.stub.php b/ext/spl/spl_fixedarray.stub.php index 7fa9b2730cf59..13f29b7a109db 100644 --- a/ext/spl/spl_fixedarray.stub.php +++ b/ext/spl/spl_fixedarray.stub.php @@ -9,6 +9,10 @@ public function __construct(int $size = 0) {} /** @tentative-return-type */ public function __wakeup(): void {} + public function __serialize(): array {} + + public function __unserialize(array $data): void {} + /** @tentative-return-type */ public function count(): int {} diff --git a/ext/spl/spl_fixedarray_arginfo.h b/ext/spl/spl_fixedarray_arginfo.h index 10c3fdb29c3bc..11ea1fdb30164 100644 --- a/ext/spl/spl_fixedarray_arginfo.h +++ b/ext/spl/spl_fixedarray_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 79a13a549d91f0e79c78a125de65fbac4795339f */ + * Stub hash: 0b508ad6499b70c92bf25960b30fefa913532a3c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_SplFixedArray___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, size, IS_LONG, 0, "0") @@ -8,6 +8,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplFixedArray___wakeup, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SplFixedArray___serialize, 0, 0, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SplFixedArray___unserialize, 0, 1, IS_VOID, 0) + ZEND_ARG_TYPE_INFO(0, data, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_SplFixedArray_count, 0, 0, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -45,12 +52,13 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_SplFixedArray_getIterator, 0, 0, Iterator, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_SplFixedArray_jsonSerialize, 0, 0, IS_ARRAY, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_SplFixedArray_jsonSerialize arginfo_class_SplFixedArray___serialize ZEND_METHOD(SplFixedArray, __construct); ZEND_METHOD(SplFixedArray, __wakeup); +ZEND_METHOD(SplFixedArray, __serialize); +ZEND_METHOD(SplFixedArray, __unserialize); ZEND_METHOD(SplFixedArray, count); ZEND_METHOD(SplFixedArray, toArray); ZEND_METHOD(SplFixedArray, fromArray); @@ -67,6 +75,8 @@ ZEND_METHOD(SplFixedArray, jsonSerialize); static const zend_function_entry class_SplFixedArray_methods[] = { ZEND_ME(SplFixedArray, __construct, arginfo_class_SplFixedArray___construct, ZEND_ACC_PUBLIC) ZEND_ME(SplFixedArray, __wakeup, arginfo_class_SplFixedArray___wakeup, ZEND_ACC_PUBLIC) + ZEND_ME(SplFixedArray, __serialize, arginfo_class_SplFixedArray___serialize, ZEND_ACC_PUBLIC) + ZEND_ME(SplFixedArray, __unserialize, arginfo_class_SplFixedArray___unserialize, ZEND_ACC_PUBLIC) ZEND_ME(SplFixedArray, count, arginfo_class_SplFixedArray_count, ZEND_ACC_PUBLIC) ZEND_ME(SplFixedArray, toArray, arginfo_class_SplFixedArray_toArray, ZEND_ACC_PUBLIC) ZEND_ME(SplFixedArray, fromArray, arginfo_class_SplFixedArray_fromArray, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) diff --git a/ext/spl/tests/SplFixedArray_serialize.phpt b/ext/spl/tests/SplFixedArray_serialize.phpt index f99812ecceb70..da7c212109ecd 100644 --- a/ext/spl/tests/SplFixedArray_serialize.phpt +++ b/ext/spl/tests/SplFixedArray_serialize.phpt @@ -13,6 +13,8 @@ $array[2] = 42; $array[3] = $obj; $array[4] = range(1, 5); +$array->foo = "bar"; + $ser = serialize($array); echo "$ser\n"; $unser = unserialize($ser); @@ -24,10 +26,24 @@ var_dump($unser[0], $unser[1], $unser[2], $unser[3], $unser[4]); $unser[4] = 'quux'; var_dump($unser[4]); +var_dump($unser->foo); + +// __unserialize is a no-op on a non-empty SplFixedArray +$array = new SplFixedArray(1); +$array->__unserialize([ + [1], + [ + "foo" => "bar", + ], +]); +var_dump($array); ?> ---EXPECT-- -O:13:"SplFixedArray":5:{i:0;s:3:"foo";i:1;N;i:2;i:42;i:3;O:8:"stdClass":1:{s:4:"prop";s:5:"value";}i:4;a:5:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;}} +--EXPECTF-- +Deprecated: Creation of dynamic property SplFixedArray::$foo is deprecated in %s on line %d +O:13:"SplFixedArray":6:{i:0;s:3:"foo";i:1;N;i:2;i:42;i:3;O:8:"stdClass":1:{s:4:"prop";s:5:"value";}i:4;a:5:{i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;}s:3:"foo";s:3:"bar";} + +Deprecated: Creation of dynamic property SplFixedArray::$foo is deprecated in %s on line %d count: 5 getSize(): 5 string(3) "foo" @@ -50,3 +66,8 @@ array(5) { int(5) } string(4) "quux" +string(3) "bar" +object(SplFixedArray)#5 (1) { + [0]=> + NULL +} diff --git a/ext/spl/tests/bug70155.phpt b/ext/spl/tests/bug70155.phpt index 0aa246cc2388f..a609205aca279 100644 --- a/ext/spl/tests/bug70155.phpt +++ b/ext/spl/tests/bug70155.phpt @@ -9,6 +9,8 @@ $data = unserialize($exploit); var_dump($data); ?> --EXPECTF-- +Deprecated: Creation of dynamic property ArrayObject::$0 is deprecated in %s on line %d + Fatal error: Uncaught InvalidArgumentException: Overloaded object of type DateInterval is not compatible with ArrayObject in %s Stack trace: %s diff --git a/ext/spl/tests/bug74669.phpt b/ext/spl/tests/bug74669.phpt index 0966a4cf27e15..597e694296d26 100644 --- a/ext/spl/tests/bug74669.phpt +++ b/ext/spl/tests/bug74669.phpt @@ -105,6 +105,8 @@ object(SelfArray)#9 (1) { string(3) "bar" } string(77) "O:9:"SelfArray":4:{i:0;i:16777216;i:1;N;i:2;a:1:{s:3:"foo";s:3:"bar";}i:3;N;}" + +Deprecated: Creation of dynamic property SelfArray::$foo is deprecated in %s on line %d object(SelfArray)#9 (1) { ["foo"]=> string(3) "bar" diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 6166cf6587bb6..7a6ee00d8e49c 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -343,6 +343,7 @@ */ const PHP_ROUND_HALF_ODD = UNKNOWN; +#[AllowDynamicProperties] final class __PHP_Incomplete_Class { } diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index d3dd087e83a83..4469a5b07ee08 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0411f358f211eb9173272c6f296899d349ab5971 */ + * Stub hash: 4df5576b4e03b18896abf58e6c70d9fd6ae76687 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -3578,7 +3578,11 @@ static zend_class_entry *register_class___PHP_Incomplete_Class(void) INIT_CLASS_ENTRY(ce, "__PHP_Incomplete_Class", class___PHP_Incomplete_Class_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); - class_entry->ce_flags |= ZEND_ACC_FINAL; + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; + + zend_string *attribute_name_AllowDynamicProperties_class___PHP_Incomplete_Class = zend_string_init_interned("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); + zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class___PHP_Incomplete_Class, 0); + zend_string_release(attribute_name_AllowDynamicProperties_class___PHP_Incomplete_Class); return class_entry; } diff --git a/ext/standard/tests/serialize/bug49649.phpt b/ext/standard/tests/serialize/bug49649.phpt index 7bbba03c1492e..310cd5b8881e3 100644 --- a/ext/standard/tests/serialize/bug49649.phpt +++ b/ext/standard/tests/serialize/bug49649.phpt @@ -33,7 +33,8 @@ class Foo $class = unserialize(base64_decode($serialized)); var_dump($class); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Creation of dynamic property Foo::$notThere is deprecated in %s on line %d object(Foo)#1 (4) { ["public"]=> int(3) diff --git a/ext/standard/tests/serialize/bug49649_1.phpt b/ext/standard/tests/serialize/bug49649_1.phpt index e4f01d3039d5a..ddaff1e960c4f 100644 --- a/ext/standard/tests/serialize/bug49649_1.phpt +++ b/ext/standard/tests/serialize/bug49649_1.phpt @@ -33,7 +33,8 @@ class Foo $class = unserialize(base64_decode($serialized)); var_dump($class); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Creation of dynamic property Foo::$notThere is deprecated in %s on line %d object(Foo)#1 (4) { ["public":protected]=> int(3) diff --git a/ext/standard/tests/serialize/bug49649_2.phpt b/ext/standard/tests/serialize/bug49649_2.phpt index 93b5e298f993e..16a9b4d7c722e 100644 --- a/ext/standard/tests/serialize/bug49649_2.phpt +++ b/ext/standard/tests/serialize/bug49649_2.phpt @@ -33,7 +33,8 @@ class Foo $class = unserialize(base64_decode($serialized)); var_dump($class); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Creation of dynamic property Foo::$notThere is deprecated in %s on line %d object(Foo)#1 (4) { ["public":"Foo":private]=> int(3) diff --git a/ext/standard/tests/serialize/bug62836_1.phpt b/ext/standard/tests/serialize/bug62836_1.phpt index 7d03e9fd187bd..480dbab1df127 100644 --- a/ext/standard/tests/serialize/bug62836_1.phpt +++ b/ext/standard/tests/serialize/bug62836_1.phpt @@ -5,7 +5,7 @@ Bug #62836 (Seg fault or broken object references on unserialize()) $serialized_object='O:1:"A":4:{s:1:"b";O:1:"B":0:{}s:2:"b1";r:2;s:1:"c";O:1:"B":0:{}s:2:"c1";r:4;}'; spl_autoload_register(function ($name) { unserialize("i:4;"); - eval("class $name {} "); + eval("#[AllowDynamicProperties] class $name {} "); }); print_r(unserialize($serialized_object)); diff --git a/ext/standard/tests/serialize/bug62836_2.phpt b/ext/standard/tests/serialize/bug62836_2.phpt index 0634b1dac135b..95bd75bb6fb87 100644 --- a/ext/standard/tests/serialize/bug62836_2.phpt +++ b/ext/standard/tests/serialize/bug62836_2.phpt @@ -8,7 +8,7 @@ ini_set('unserialize_callback_func','mycallback'); function mycallback($classname) { unserialize("i:4;"); - eval ("class $classname {} "); + eval ("#[AllowDynamicProperties] class $classname {} "); } print_r(unserialize($serialized_object)); diff --git a/ext/standard/tests/serialize/bug72663.phpt b/ext/standard/tests/serialize/bug72663.phpt index c50591ca963f4..9c006d50d80ba 100644 --- a/ext/standard/tests/serialize/bug72663.phpt +++ b/ext/standard/tests/serialize/bug72663.phpt @@ -3,6 +3,7 @@ Bug #72663 (1): Don't call __destruct if __wakeup not called or fails --FILE-- +--EXPECT-- +object(__PHP_Incomplete_Class)#1 (2) { + ["__PHP_Incomplete_Class_Name"]=> + string(1) "C" + ["p"]=> + int(1) +} diff --git a/ext/standard/tests/serialize/unserialize_overwrite_undeclared_protected.phpt b/ext/standard/tests/serialize/unserialize_overwrite_undeclared_protected.phpt index b442c922c4153..d9f44c37a1560 100644 --- a/ext/standard/tests/serialize/unserialize_overwrite_undeclared_protected.phpt +++ b/ext/standard/tests/serialize/unserialize_overwrite_undeclared_protected.phpt @@ -12,7 +12,8 @@ O:4:"Test":2:{s:4:"\0*\0x";N;s:4:"\0*\0x";N;} STR; var_dump(unserialize($str)); ?> ---EXPECT-- +--EXPECTF-- +Deprecated: Creation of dynamic property Test::$x is deprecated in %s on line %d object(Test)#1 (2) { ["foo"]=> NULL diff --git a/ext/standard/tests/serialize/unserialize_ref_to_overwritten_declared_prop.phpt b/ext/standard/tests/serialize/unserialize_ref_to_overwritten_declared_prop.phpt index f32b0c12e2f26..c4c3c957d3ee8 100644 --- a/ext/standard/tests/serialize/unserialize_ref_to_overwritten_declared_prop.phpt +++ b/ext/standard/tests/serialize/unserialize_ref_to_overwritten_declared_prop.phpt @@ -3,10 +3,10 @@ Trying to create a reference to an overwritten declared property --FILE-- --EXPECTF-- -Notice: unserialize(): Error at offset 51 of 52 bytes in %s on line %d +Notice: unserialize(): Error at offset 36 of 52 bytes in %s on line %d bool(false) diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 0556d5522ce5d..72d50c190e653 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -641,6 +641,18 @@ declared_property: int ret = is_property_visibility_changed(obj->ce, &key); if (EXPECTED(!ret)) { + if (UNEXPECTED(obj->ce->ce_flags & ZEND_ACC_NO_DYNAMIC_PROPERTIES)) { + zend_throw_error(NULL, "Cannot create dynamic property %s::$%s", + ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + goto failure; + } else if (!(obj->ce->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES)) { + zend_error(E_DEPRECATED, "Creation of dynamic property %s::$%s is deprecated", + ZSTR_VAL(obj->ce->name), zend_get_unmangled_property_name(Z_STR_P(&key))); + if (EG(exception)) { + goto failure; + } + } + data = zend_hash_add_new(ht, Z_STR(key), &EG(uninitialized_zval)); } else if (ret < 0) { goto failure;