[php-src] Issue #24389: JIT: segfault when an inlined call's reference argument changes from int to float (zend_jit_trace_exit assertion)

From: Date: Sat, 17 Oct 2026 22:07:28 +0000
Subject: [php-src] Issue #24389: JIT: segfault when an inlined call's reference argument changes from int to float (zend_jit_trace_exit assertion)
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253014@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24389 Author: waroad With the tracing JIT, a function called in a hot loop with reference-bound variables crashes once the operand type changes from int to float: the release build segfaults and a debug build fails an assertion in zend_jit_trace_exit. ```php <?php function right($x, $y) { $a = $x + $y; return $x - $y; } function driver($x, $y) { $xr = &$x; $yr = &$y; for ($i = 0; $i < 300; $i++) { right($x, $y); } } driver(3, 2); driver(3.5, 2.5); ``` ``` php -n -d opcache.enable_cli=1 -d opcache.file_update_protection=0 -d opcache.jit_buffer_size=64M -d opcache.jit=tracing input.php ``` Debug build (assertions enabled), same command run with USE_ZEND_ALLOC=0 USE_TRACKED_ALLOC=1: ``` php: ext/opcache/jit/zend_jit_trace.c:8858: zend_jit_trace_exit: Assertion `((execute_data)->opline) >= ((execute_data)->func)->op_array.opcodes && ((execute_data)->opline) < ((execute_data)->func)->op_array.opcodes + ((execute_data)->func)->op_array.last' failed. Aborted ``` Release build: exit code 139, "Segmentation fault". The script has no output and should simply run to completion. A more natural shape that also segfaults the release build (after printing 44850, where JIT off prints 44850 and 45000): foreach ($data as &$v) { $sum += calc($v, 1); } over an int array and then a float array, with calc doing the same arithmetic as right. It still reproduces at 5ccc56b1e43f (latest upstream when checked): the release build segfaults (SIGSEGV) with the command above, and the debug build fails the same assertion (at line 8850 there; checked with the hot thresholds set to 1). The command uses the default JIT thresholds. Looks like the type guard emitted for the register-allocated callee CVs on ZEND_JIT_TRACE_ENTER in zend_jit_trace.c (zend_jit_type_guard(&ctx, NULL, ...)) has no opline, so the exit trusts EX(opline) of the freshly pushed inlined frame, which is not initialised yet. PR #18392 (JIT: Don't reuse IP register for EX(call)) may be related; I did not test it, its diff does not apply on the latest commit. 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 (#253014) next »