[php-src] Issue #8079: Recent patch to SplFixedArray causes array_walk + json encode to hang
| From: | TysonAndre | Date: | Fri, 11 Feb 2022 13:55:46 +0000 |
| Subject: | [php-src] Issue #8079: Recent patch to SplFixedArray causes array_walk + json encode to hang | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-239671@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/8079
Author: 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 issues affect 52ae6417ca3e62eff796bf3e3b5662fe6a9ee085 and newer after the fix to GH-8044
- Because the properties table was getting cloned, I assume the iterator is getting affected.
- Z_OBJPROP_P calls get_properties, which now replaces the original properties table with a clone
and returns the original property table to the caller.
- array_walk's implementation calls Z_OBJPROP_P in a loop.
The following code:
```php
<?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";
});
});
?>
--EXPECTF--
```
Resulted in this output:
```c
// 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:
```c
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(52ae6417c or newer)
### Operating System
_No response_