Skip to content

Recent patch to SplFixedArray causes array_walk + json encode to hang, circular var_export during gc to run out of memory #8079

Description

@TysonAndre

Description

I believe that a lot of code in php-src/pecls relies on the fact that the pointer of Z_OBJPROP_P doesn't change, for non-empty properties tables. There's may be more examples than just array_walk but I'm still trying to create one for var_export

The following code:

<?phpcall_user_func(function () {
$x = newSplFixedArray(2);
$x[0] = $x;
$x[1] = strtoupper('foo');
echo"Calling array_walk\n";
array_walk($x, function ($value, $index) use($x) {
printf("Value=%s index=%s\n", json_encode($value), json_encode($index));
echo"Calling json_encode\n";
echojson_encode($x, JSON_PARTIAL_OUTPUT_ON_ERROR), "\n";
});
});

Resulted in this output:

// PHP hangs in an infinite loop.// Adding debug statements to spl_fixedarray_object_get_properties indicates it is repeatedly called and cloning the array in 52ae6417ca3e62eff796bf3e3b5662fe6a9ee085

But I expected this output instead:

Callingarray_walkValue=index=0Callingjson_encode
[null,"FOO"]
Value="FOO"index=1Callingjson_encode
[null,"FOO"]

Relevant parts of the code - zend_hash_iterator_pos probably resets to the start if Z_OBJPROP is a different ht from when starting.

// Zend/zend_hash.c
ZEND_API HashPosition ZEND_FASTCALL zend_hash_iterator_pos(uint32_t idx, HashTable *ht)
{
HashTableIterator *iter = EG(ht_iterators) + idx;
ZEND_ASSERT(idx != (uint32_t)-1);
if (UNEXPECTED(iter->ht != ht)) {
// ext/standard/array.c
static int php_array_walk(zval *array, zval *userdata, int recursive) /* {{{ */
// ...
} else if (Z_TYPE_P(array) == IS_OBJECT) {
target_hash = Z_OBJPROP_P(array);
pos = zend_hash_iterator_pos(ht_iter, target_hash);

PHP Version

PHP 8.0.17-dev(52ae641 or newer)

Operating System

No response

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions