Fix GH-9186 @strict-properties can be bypassed using unserialization (#9354)

* Emit deprecation warnings when adding dynamic properties to classes during unserialization - this will become an Error in php 9.0.
  (Adding dynamic properties in other contexts was already a deprecation warning - the use case of unserialization was overlooked)
* Throw an error when attempting to add a dynamic property to a `readonly` class when unserializing
* Add new serialization methods `__serialize`/`__unserialize` for SplFixedArray to avoid creating deprecated dynamic
  properties that would then be added to the backing fixed-size array
* Don't add named dynamic/declared properties (e.g. $obj->foo) of SplFixedArray to the backing array when unserializing
* Update tests to declare properties or to expect the deprecation warning
* Add news entry

Co-authored-by: Tyson Andre <tysonandre775@hotmail.com>
This commit is contained in:
Máté Kocsis 2022-08-30 13:46:32 +02:00 committed by GitHub
parent 8d78dce902
commit adb45a63c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 271 additions and 22 deletions

View file

@ -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;