Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Reduce memory usage when unserializing packed arrays of size 1
Previously, PHP's unserialize() would unconditionally convert a packed array to
an associative(hashed) array to solve the problem of `[0 => $v1, 'key' => $v2]`
starting out as a packed array but becoming an associative array,
causing the raw zval pointer to the value for $v1 to have changed.

- This can only happen when there's at least two elements, though.

Additionally, reduce memory usage when calling `__unserialize` with arrays of
size 0 or 1
(e.g. for user-defined functions that store the passed in array as a property)
  • Loading branch information
TysonAndre committed Nov 26, 2021
commit 0e9bce65e43bb2b813fa33ed45de7f64e2c88dcd
32 changes: 22 additions & 10 deletions ext/standard/var_unserializer.re
Original file line number Diff line number Diff line change
Expand Up @@ -769,14 +769,22 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
return 0;
}

array_init_size(&ary, elements);
/* Avoid reallocation due to packed -> mixed conversion. */
zend_hash_real_init_mixed(Z_ARRVAL(ary));
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
ZVAL_DEREF(rval);
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
zval_ptr_dtor(&ary);
return 0;
if (elements > 0) {
/* Avoid reallocation due to packed -> mixed conversion.
* Note that an array must have at least 2 elements in order for the packed -> mixed conversion to change zval locations.
*/
array_init_size(&ary, elements);
if (elements > 1) {
zend_hash_real_init_mixed(Z_ARRVAL(ary));
}
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
ZVAL_DEREF(rval);
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
zval_ptr_dtor(&ary);
return 0;
}
} else {
ZVAL_EMPTY_ARRAY(&ary);
}

/* Delay __unserialize() call until end of serialization. We use two slots here to
Expand Down Expand Up @@ -1078,8 +1086,12 @@ use_double:
if (elements) {
array_init_size(rval, elements);
/* we can't convert from packed to hash during unserialization, because
reference to some zvals might be kept in var_hash (to support references) */
zend_hash_real_init_mixed(Z_ARRVAL_P(rval));
* references to some zvals might be kept in var_hash (to support references).
* This will only change zval addresses if there are at least 2 values
* (the value being referenced, and the value being added afterwards) */
if (elements > 1) {
zend_hash_real_init_mixed(Z_ARRVAL_P(rval));
}
} else {
ZVAL_EMPTY_ARRAY(rval);
return finish_nested_data(UNSERIALIZE_PASSTHRU);
Expand Down