[php-src] Issue #24488: 8.4.27 / 8.5.12 regression: GC double-frees the SimpleXMLElement properties table (`zend_mm_heap corrupted`), introduced by
the GH-24050 fix

From: Date: Wed, 21 Oct 2026 17:42:49 +0000
Subject: [php-src] Issue #24488: 8.4.27 / 8.5.12 regression: GC double-frees the SimpleXMLElement properties table (`zend_mm_heap corrupted`), introduced by
the GH-24050 fix
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253055@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24488 Author: evankford ### Description ## Description The following code: ```php <?php for ($i = 0; $i < 2; $i++) { $xml = new SimpleXMLElement('<a><b/></a>'); get_object_vars($xml); $holder = new stdClass; $holder->xml = $xml; $holder->self = $holder; unset($xml, $holder); gc_collect_cycles(); $junk[] = str_repeat('x', 100); } echo "done\n"; ``` Resulted in this output: ``` zend_mm_heap corrupted ``` But I expected this output instead: ``` done ``` It fails every time on 8.4.27 and 8.5.12 (official php:*-cli Docker images, aarch64) and on a macOS NTS build of 8.4.27. It passes on 8.4.26. Each of these is required to trigger it: - Something that materializes the SimpleXML properties table (get_object_vars(), an (array) cast, or json_encode()). foreach or plain property access does not trigger it. - The SimpleXMLElement must be reachable from a cycle that the GC collects. With the system allocator, a single GC run is enough. Here is a one-shot version: ```php <?php $xml = new SimpleXMLElement('<a><b/></a>'); get_object_vars($xml); $holder = new stdClass; $holder->xml = $xml; $holder->self = $holder; unset($xml, $holder); gc_collect_cycles(); ``` ``` $ USE_ZEND_ALLOC=0 php one-shot.php free(): double free detected in tcache 2 ``` Valgrind output (8.4.27 built with -g -O2, USE_ZEND_ALLOC=0): ``` Invalid read of size 4 at zend_gc_collect_cycles (zend_gc.c:2070) by zif_gc_collect_cycles (zend_builtin_functions.c:124) Address 0xb4b0324 is 4 bytes inside a block of size 56 free'd at free by zend_gc_collect_cycles (zend_gc.c:2081) <- obj->handlers->free_obj(obj) Block was alloc'd at by _zend_new_array_0 (zend_hash.c:327) by sxe_get_prop_hash (simplexml.c:1096) by zend_std_get_properties_for (zend_object_handlers.c:2643) by zif_get_object_vars (zend_builtin_functions.c:801) Invalid write of size 4 at zend_gc_collect_cycles (zend_gc.c:2089) ... (26 invalid accesses total) ``` **Bisected cause:** the hunk added to gc_collect_white() by 7b75d71ca3f6 (GH-24050, PR #24061): ```c if (GC_REF_CHECK_COLOR(ht, GC_WHITE)) { GC_REF_SET_BLACK(ht); + if (!GC_INFO(ht)) { + gc_add_garbage((zend_refcounted *)ht); + } ``` I built 8.4.27 from the release tarball twice. The unmodified build crashes. The same build with only those three lines removed prints done and is clean under valgrind. The table that get_gc returns is now added to the garbage buffer. sxe_get_gc() returns sxe->properties, a table that SimpleXML owns. sxe_object_free_storage() releases it with zend_hash_release() while the GC is freeing the object. The GC then reaches the table's own garbage slot and reads, destroys and frees memory that has already been freed. Using the same pattern with stdClass (dynamic properties), ArrayObject and DateTime is clean under valgrind. Any other extension whose get_gc returns a table it owns and releases itself may be affected the same way. **Real-world impact:** our Laravel/Pest suite crashes paratest workers with zend_mm_heap corrupted (exit 134) on 8.4.27. Mockery expectations return collections of SimpleXMLElements, the mock and its expectations form a cycle, and PHPUnit's comparator/exporter reads all properties of the elements. We have pinned CI to 8.4.26. ### PHP Version ```plain PHP 8.4.27 (cli) (built: Oct 21 2026 15:54:26) (NTS) Copyright (c) The PHP Group Built by https://github.com/docker-library/php Zend Engine v4.4.27, Copyright (c) Zend Technologies with Zend OPcache v8.4.27, Copyright (c), by Zend Technologies Also reproduces on 8.5.12 (php:8.5-cli). Not reproducible on 8.4.26. ``` ### Operating System Debian (official php:8.4.27-cli Docker image, aarch64); also macOS (Laravel Herd build)

« previous php.bugs (#253055) next »