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

Closed
@TysonAndre

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:

<?php
call_user_func(function () {
    $x = new SplFixedArray(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";
        echo json_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:

Calling array_walk
Value= index=0
Calling json_encode
[null,"FOO"]
Value="FOO" index=1
Calling json_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