[php-src] Issue #24438: Tracing JIT: segfault on $f($arr[k]) when $f alternates between by-value and by-reference functions

From: Date: Mon, 19 Oct 2026 13:57:48 +0000
Subject: [php-src] Issue #24438: Tracing JIT: segfault on $f($arr[k]) when $f alternates between by-value and by-reference functions
Groups: php.bugs 
Request: Send a blank email to php-bugs+get-253030@lists.php.net to get a copy of this message
Issue: https://github.com/php/php-src/issues/24438 Author: waroad With the tracing JIT, a dynamic call $f($arr[k]) where $f alternates between a by-value and a by-reference function crashes. ```php <?php function byval($a) { return (int)$a; } function byref(&$a) { return (int)$a; } $fns = ["byval", "byref"]; $arr = []; for ($i = 0; $i < 120; $i++) { $f = $fns[$i % 2]; $f($arr[1]); $f($arr[0]); } ``` ``` php -n -d opcache.enable=1 -d opcache.enable_cli=1 -d opcache.file_update_protection=0 -d opcache.jit_buffer_size=64M -d opcache.jit=tracing -d opcache.jit_hot_loop=1 -d opcache.jit_hot_func=1 -d opcache.jit_hot_return=1 -d opcache.jit_hot_side_exit=1 -d opcache.jit_max_polymorphic_calls=0 reduced.php ``` Debug build: ``` Warning: Undefined array key 1 in reduced.php on line 5 Warning: Undefined array key 0 in reduced.php on line 5 php: Zend/zend_operators.c:1016: zval_get_long_func: Assertion `0' failed. ``` Release build: the same two warnings, then SIGSEGV (exit 139). With opcache.jit=off the script only prints the two warnings and exits normally, so I expected the same under the JIT. jit_max_polymorphic_calls=0 is not required: with the default value, a variant with five callees (four by-value, one by-ref) and a defined $arr = [5, 6] crashes the same way. The hot counters of 1 are needed to form the traces in a short script. It reproduces at 9253b03bef3f7cb96ac2594c23382b0ba5ab6e19 (latest upstream when checked): the debug build hits the assertion above, the release build segfaults (rc 139). Looks like the megamorphic INIT_DYNAMIC_CALL (call->func unknown) makes zend_jit_trace.c skip the specialised FETCH_DIM_FUNC_ARG, but zend_jit_send_var (zend_jit_ir.c, ZEND_SEND_FUNC_ARG) still compiles the by-ref branch using the recorded type of the fetched value, which does not include MAY_BE_INDIRECT. ZEND_FETCH_OBJ_FUNC_ARG has a zend_jit_func_arg_by_ref_guard for this case and the DIM variant does not. This is from reading jit_debug trace output, not confirmed in a debugger. Open PR #18392 (reworks EX(call) handling in the call/send path) 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 (#253030) next »