[php-src] Issue #8044: var_export emits debug assertion errors for `HT_ASSERT_RC1(ht);` in builds for SplFixedArray circular references

From: Date: Sat, 05 Feb 2022 19:15:08 +0000
Subject: [php-src] Issue #8044: var_export emits debug assertion errors for `HT_ASSERT_RC1(ht);` in builds for SplFixedArray circular references
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-239505@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/8044
Author: TysonAndre

### Description

The following code in a php build configured with --enable-debug (reproduced in 7.4,
8.0, 8.1, 8.2.0-dev):

```php
<?php
$x = new SplFixedArray(1);
$x[0] = $x;
var_export($x);
```

Resulted in this output in _zend_hash_index_add_or_update_i for
HT_ASSERT_RC1. This is due to ext/spl/spl_array.c needing to update the implementation
of its array in place for static HashTable* spl_fixedarray_object_get_properties(zend_object
*obj) so that the output of var_export, var_dump, json_encode, etc. all reflect the latest
array contents of the backing fixed array:

```
php: /path/to/php-src/Zend/zend_hash.c:1012: _zend_hash_index_add_or_update_i: Assertion
`(zend_gc_refcount(&(ht)->gc) == 1) || ((ht)->u.flags & (1<<6))' failed.
[1]    312613 abort (core dumped)  php -a
```

But I expected this output instead:
```
Not crashing, successfully setting $x[0]
```

(I haven't tested php 7.3, though that branch is closed regardless. 7.2 did not have this debug
failure)

--------

Also, I'm not familiar with the history of this, but it seems like there are (at least) **3
different mechanisms** in use for infinite recursion detection for json_encode/var_export/var_dump.
**Would it make sense to firstcheck Z_PROTECT_RECURSION in var_export in addition to/instead of
protecting the array, before calling get_properties_for?**

- json_encode needs distinct results of *get_properties (Z_OBJPROP) to detect infinite
recursion.
- var_export needs *get_properties_for to return the same array every time to detect
infinite recursion with GC_TRY_PROTECT_RECURSION. (E.g. this 1. makes it impractical to save memory
(and save time in garbage collection) by returning a distinct array rather than populating
get_properties., 2. Seems like it would delay garbage collection of objects unintuitively if you
call var_export then replace the entry of SplFixedArray (they wouldn't be garbage collected
until the next var_export/json_encode/var_dump call, or (array) cast?))

  Adding similar calls to Z_PROTECT_RECURSION(zval *struct) (and UNPROTECT), either
instead of or in addition to existing  may help avoid this failure mode.

  By design, var_export does not use https://www.php.net/manual/en/language.oop5.magic.php#object.debuginfo
- I wonder if that's somehow related to the different design used by var_dump, but the code
isn't documented
- var_dump is doing yet another different thing, and calling Z_PROTECT_RECURSION(zval
*struct) to set the GC_FLAGS
on the object?
- serialize thankfully calls php_var_serialize_intern and checks with var_already
= php_add_var_hash with a var_hash specific to that serializer, which is correct

-------

I'd be worried about replacing the existing checks in <=8.1/8.0, in case extensions were
somehow relying on specifics of whatever calls to get_properties/get_properties_for were made.

### PHP Version

PHP 7.4-8.2

### Operating System

_No response_


Thread (1 message)

  • TysonAndre
« previous php.bugs (#239505) next »