[php-src] Issue #24388: Tracing JIT: segfault when a trace runs out of side exits (switch with ~500 cases followed by ++$a)

From: Date: Sat, 17 Oct 2026 22:04:48 +0000
Subject: [php-src] Issue #24388: Tracing JIT: segfault when a trace runs out of side exits (switch with ~500 cases followed by ++$a)
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253013@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24388 Author: waroad With the tracing JIT, a hot function containing a switch with about 500 cases followed by ++$a crashes PHP at JIT-compile time (SIGSEGV in a release build, assertion failure in a debug build). A testing tool found this; it is not from a real program. The 11 KB reproducer is generated (507 cases; the case count matters, 480-506 and 511-530 do not trigger it with this shape): ```php <?php $s = '<?php' . "\n" . 'function f(int $k,int $a) {switch($k) {'; for ($i = 0; $i < 507; $i++) $s .= "case $i:\$l=$i;break;"; $s .= '}return ++$a;}' . "\n" . 'for($i=0;$i<200;$i++) {$a=3;f(0,$a);}' . "\n"; file_put_contents('input.php', $s); ``` ``` php gen.php php -n -d opcache.enable_cli=1 -d opcache.jit_buffer_size=64M -d opcache.jit=tracing -d opcache.file_update_protection=0 input.php ``` Debug build (backtrace from the earlier build where it was found; the assertion is the same at 5ccc56b1e43f): ``` php: ext/opcache/jit/zend_jit_ir.c:627: jit_SNAPSHOT: Assertion `exit_point != -1' failed. jit_SNAPSHOT zend_jit_ir.c:627 _ir_GUARD_NOT ir/ir.c:3501 zend_jit_inc_dec zend_jit_ir.c:4765 zend_jit_trace zend_jit_trace.c:4501 zend_jit_compile_root_trace zend_jit_trace.c:7571 ``` I expected the JIT to give up on the trace (TOO_MANY_EXITS) and the script to finish normally, as it does with opcache.jit=off (exit 0). It reproduces at 5ccc56b1e43f: the debug build aborts with the assertion above (exit 134), the release build segfaults (exit 139) with no output. Looks like zend_jit_inc_dec() in ext/opcache/jit/zend_jit_ir.c does not check the result of zend_jit_trace_get_exit_addr(), which is NULL once the trace has used its ZEND_JIT_TRACE_MAX_EXITS (512) exits, so it emits a guard to address 0; the neighbouring code does if (!exit_addr) return 0;. Open PR #18392 may be related; I did not test it. I can send a patch if that helps. Found with an LLM-based testing tool; I used Claude to reduce it and look for the cause.

« previous php.bugs (#253013) next »