Category
Regression: · runtime crash · child of #36188
Problem
Verified on master 4eed6a2785 in the pinned image (build/audit/v_gc1.php):
<?php class N { public $o; }
for ($r = 0; $r < 2; $r++) {
for ($i = 0; $i < 1000; $i++) { $a = new N; $b = new N; $a->o = $b; $b->o = $a; }
echo gc_collect_cycles(), "\n";
}
|
Zend |
AOT |
| output |
1998 / 2000 |
Segmentation fault (rc 139), no output |
With 100,000 pairs per round (build/audit/p_gc.php) the binary additionally prints PHP Warning: Undefined variable $a in … on line 1 before crashing — $a is assigned on every iteration, so the warning is the module-global undefined-variable flag (#36190) misfiring on a {main} loop variable. The 65,536-object hard cap in GcCollectCyclesRuntime::MAX_OBJECTS (#36195) is a separate defect; 2,000 objects are far below it, so this crash is in the mark/sweep itself (phpc_gc_collect_cycles_impl) or in the property-count/phpc_gc_prop_counts bookkeeping for objects whose only property is another object.
gc_collect_cycles() is called by frameworks, test runners and long-running workers; any AOT binary doing so with live cycles dies.
php-src reference
- Zend/zend_gc.c
zend_gc_collect_cycles — mark roots grey, scan, collect white; refcounts of the objects being freed are decremented through zend_objects_store_del, never read after free.
PHP implementation target
Repro
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/compile.php -o build/gc1 build/audit/v_gc1.php && ./build/gc1; echo rc=$?; php build/audit/v_gc1.php'
Done when
Category
Regression:· runtime crash · child of #36188Problem
Verified on master
4eed6a2785in the pinned image (build/audit/v_gc1.php):1998/2000With 100,000 pairs per round (
build/audit/p_gc.php) the binary additionally printsPHP Warning: Undefined variable $a in … on line 1before crashing —$ais assigned on every iteration, so the warning is the module-global undefined-variable flag (#36190) misfiring on a{main}loop variable. The 65,536-object hard cap inGcCollectCyclesRuntime::MAX_OBJECTS(#36195) is a separate defect; 2,000 objects are far below it, so this crash is in the mark/sweep itself (phpc_gc_collect_cycles_impl) or in the property-count/phpc_gc_prop_countsbookkeeping for objects whose only property is another object.gc_collect_cycles()is called by frameworks, test runners and long-running workers; any AOT binary doing so with live cycles dies.php-src reference
zend_gc_collect_cycles— mark roots grey, scan, collect white; refcounts of the objects being freed are decremented throughzend_objects_store_del, never read after free.PHP implementation target
PHP_COMPILER_LLVM_ASSERT=1andgdb -batch -ex run -ex bt(recipe indocs/AGENT-HANDOVER-2026-08-17.md); the audit log shows the crash is deterministic.test/differential/cases/(gc_collect_cyclesreturn value must match Zend) and agc_status()parity case.Repro
./script/docker-exec.sh -- bash -lc 'source script/php-env.sh && php bin/compile.php -o build/gc1 build/audit/v_gc1.php && ./build/gc1; echo rc=$?; php build/audit/v_gc1.php'Done when
1998/2000under AOT; the 100k probe prints no warning and matches Zend's counts (with Runtime: fixed static arenas give hello-world a 70 MB .bss and the cycle collector silently stops tracking objects after 65,536 (lib/JIT/Builtin/GcCollectCyclesRuntime.php, lib/JIT/Builtin/StreamGlobalsJit.php) #36195) or at least does not crashscript/differential-sweep.sh --aot --repeat 10unchanged plus the new cases green