[php-src] Issue #18833: Use after free with weakmaps dependent on destruction order
| From: | danog | Date: | Wed, 11 Jun 2025 11:38:23 +0000 |
| Subject: | [php-src] Issue #18833: Use after free with weakmaps dependent on destruction order | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-250242@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/18833
Author: danog
### Description
The following code:
```php
<?php
class a {
public static WeakMap $map;
public static Generator $storage;
}
a::$map = new WeakMap;
$closure = function () {
$obj = new a;
a::$map[$obj] = true;
yield $obj;
};
a::$storage = $closure();
a::$storage->current();
```
Causes a use after free, due to the following destruction order in
zend_objects_store_free_object_storage:
```
1. a
2. Generator
3. Closure
4. WeakMap
```
At 1, obj->handlers->free_obj is actually never invoked for a and
only the flag is set, because normally we're in the fast fast_shutdown path, which
skips invoking free_obj for standard PHP objects (thus, the weak map is not notified).
At 2, the zend_generator_free_storage procedure causes a to be actually
freed with efree, since it is only referenced by the generator, which is being freed; thus,
a is actually freed without ever notifying the weak map.
At 4, the weak map accesses the already freed object while iterating over the members.
The issue was particularly nasty to reproduce as it won't reproduce by just disabling the zend
allocator and enabling ASAN, as the fast shutdown path is for some reason disabled when using custom
allocators (will submit a PR to fix that on ASAN).
https://github.com/phpredis/phpredis/issues/2630
is a direct consequence of this bug.
Submitting a PR with a fix, and another PR with misc related improvements.
### PHP Version
```plain
PHP 8.3.22+
```
### Operating System
_No response_