[GIT-PULLS] [php-src] PR #22714: Fix use-after-free serializing an array grown by an element's hook
[email protected] (iliaal)
| Newsgroups | php.git-pulls |
|---|---|
| Message-ID | <BDXunTKkKGLhDD6ViPPl0bAZKo9TzSwKh2klz1CLtUA@main.internal.php.net> |
Pull Request: https://github.com/php/php-src/pull/22714
Author: iliaal
The IS_ARRAY case of php_var_serialize_intern() walks the array's HashTable without holding a reference while php_var_serialize_nested_data() recurses into user hooks, so a __serialize() that grows the same array through a by-reference alias reallocs the backing store mid-walk and the iterator then reads freed memory. The object case already holds a ref (via zend_get_properties_for), as do var_dump and var_export; this adds the same GC_ADDREF/GC_DTOR_NO_REF around the array walk, so the mutating append separates a copy instead of reallocating in place. __sleep() and Serializable::serialize() reach the same walk, so the single test covers all three. Only an attacker-defined mutating hook can trigger this, so it is a memory-safety bug, not a security issue.
Reproducer (invalid read in php_var_serialize_nested_data under ASan/valgrind; a normal build usually reads the freed memory and passes, so the test is an ASan canary):
```php
<?php
class G {
public $ref;
public function __serialize(): array {
for ($i = 0; $i < 128; $i++) $this->ref[] = 'x' . $i;
return ['d' => 1];
}
}
$g = new G();
$inner = [$g, 'tail'];
$g->ref = &$inner;
$top = [&$inner];
serialize($top);
```