Skip to content

JIT: segfault when an inlined call's reference argument changes from int to float (zend_jit_trace_exit assertion) #24389

Open

Description

@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
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 5ccc56b (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.

Activity

  1. ndossche commented on Oct 18, 2026

    @ndossche
    Member

    I can send a patch if that helps.

    Feel free to do so

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions