Skip to content

Commit 0e9bce6

Browse files
committed
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)
1 parent 8b86af4 commit 0e9bce6

File tree

1 file changed

+22
-10
lines changed

1 file changed

+22
-10
lines changed

ext/standard/var_unserializer.re

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -769,14 +769,22 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool
769769
return 0;
770770
}
771771

772-
array_init_size(&ary, elements);
773-
/* Avoid reallocation due to packed -> mixed conversion. */
774-
zend_hash_real_init_mixed(Z_ARRVAL(ary));
775-
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
776-
ZVAL_DEREF(rval);
777-
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
778-
zval_ptr_dtor(&ary);
779-
return 0;
772+
if (elements > 0) {
773+
/* Avoid reallocation due to packed -> mixed conversion.
774+
* Note that an array must have at least 2 elements in order for the packed -> mixed conversion to change zval locations.
775+
*/
776+
array_init_size(&ary, elements);
777+
if (elements > 1) {
778+
zend_hash_real_init_mixed(Z_ARRVAL(ary));
779+
}
780+
if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) {
781+
ZVAL_DEREF(rval);
782+
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
783+
zval_ptr_dtor(&ary);
784+
return 0;
785+
}
786+
} else {
787+
ZVAL_EMPTY_ARRAY(&ary);
780788
}
781789

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

0 commit comments

Comments
 (0)