[php-src] Issue #24438: Tracing JIT: segfault on $f($arr[k]) when $f alternates between by-value and by-reference functions
| From: | waroad | 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.