[php-src] Issue #24729: Use-after-free in ReflectionClass::setStaticPropertyValue() when the old value's destructor unbinds the property
| From: | IzaiahSun | Date: | Mon, 26 Oct 2026 08:48:01 +0000 |
| Subject: | [php-src] Issue #24729: Use-after-free in ReflectionClass::setStaticPropertyValue() when the old value's destructor unbinds the property | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-253207@lists.php.net to get a copy of this message | ||
Issue: https://github.com/php/php-src/issues/24729
Author: IzaiahSun
### Description
The following code:
```php
<?php
class A { public static $x; }
class Old {
public function __destruct() {
unset($GLOBALS['y']);
$z = 1;
A::$x = &$z;
}
}
$y = new Old;
A::$x = &$y;
(new ReflectionClass('A'))->setStaticPropertyValue('x',
str_repeat('A', 64));
```
Resulted in this output:
```
==72025==ERROR: AddressSanitizer: heap-use-after-free on address 0x6040000291d0 at pc 0x0001011972f4
bp 0x00016fc83d20 sp 0x00016fc83d18
READ of size 4 at 0x6040000291d0 thread T0
#0 zend_gc_delref zend_types.h:1370
#1 zend_objects_store_del zend_objects_API.c:182
#2 rc_dtor_func zend_variables.c:57
#3 i_zval_ptr_dtor zend_variables.h:45
#4 zval_ptr_dtor zend_variables.c:84
#5 zim_ReflectionClass_setStaticPropertyValue php_reflection.c:4455
#6 ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER zend_vm_execute.h:1907
#7 execute_ex zend_vm_execute.h:58729
#8 zend_execute zend_vm_execute.h:64381
#9 zend_execute_script zend.c:1943
#10 php_execute_script_ex main.c:2623
freed by thread T0 here:
#0 free(libclang_rt.asan_osx_dynamic.dylib:arm64e)
#1 __zend_free zend_alloc.c:3728
#2 _efree zend_alloc.c:2889
#3 zend_objects_store_del zend_objects_API.c:200
#4 zend_object_release zend_objects_API.h:77
#5 zend_objects_destroy_object zend_objects.c:209
#6 zend_objects_store_del zend_objects_API.c:181
#7 rc_dtor_func zend_variables.c:57
#8 i_zval_ptr_dtor zend_variables.h:45
#9 zval_ptr_dtor zend_variables.c:84
#10 zim_ReflectionClass_setStaticPropertyValue php_reflection.c:4455
SUMMARY: AddressSanitizer: heap-use-after-free zend_types.h:1370 in zend_gc_delref
```
But I expected this output instead:
```
(no output, clean exit)
```
---
Debug + ASan build, run with
USE_ZEND_ALLOC=0.
ReflectionClass::setStaticPropertyValue() destroys the old value of the static property
before storing the new one, while variable_ptr still points into the reference it is
about
to write. If the old value's destructor drops the last holder of that reference, the write
lands in freed memory.
ext/reflection/php_reflection.c:
```c
zval_ptr_dtor(variable_ptr);
ZVAL_COPY(variable_ptr, value);
```
### PHP Version
```plain
PHP 8.4.28-dev (cli) (built: Oct 25 2026 01:02:13) (NTS DEBUG)
Copyright (c) The PHP Group
Zend Engine v4.4.28-dev, Copyright (c) Zend Technologies
Also reproduced on master (29b5e168bf):
PHP 8.7.0-dev (cli) (built: Oct 24 2026 21:13:17) (NTS DEBUG)
Copyright © The PHP Group and Contributors
Zend Engine v4.7.0-dev, Copyright © Zend by Perforce
with Zend OPcache v8.7.0-dev, Copyright ©, by Zend by Perforce
```
### Operating System
macOS 27.0.1 (arm64)