Skip to content

JIT: Fix function JIT level 1 crash on FETCH_OBJ_FUNC_ARG of a hooked property - #24402

Open
waroad wants to merge 2 commits into
php:PHP-8.4from
waroad:traj-s-20261017-073746-01-ns-hooked-prop-arg-segv-pr1
Open

waroad wants to merge 2 commits into
php:PHP-8.4from
waroad:traj-s-20261017-073746-01-ns-hooked-prop-arg-segv-pr1

Conversation

@waroad

@waroad waroad commented Oct 18, 2026

Copy link
Copy Markdown

At JIT optimization level 1 (e.g. opcache.jit=1201), passing a property with a get hook to a function that is only resolved at run time segfaults on the second call. The level-1 path in zend_jit() calls the VM handler for each opcode. After ZEND_FETCH_OBJ_R it checks whether the handler took the SIMPLE_GET fast path and pushed the getter frame, and if so it exits to the VM. ZEND_FETCH_OBJ_FUNC_ARG uses the same FETCH_OBJ_R handler for by-value sends, but it went through the generic default: case without that check. The JIT'd code then carried on to SEND_FUNC_ARG while the getter frame was active, and EX(call) was NULL.

The fix adds ZEND_FETCH_OBJ_FUNC_ARG to the ZEND_FETCH_OBJ_R case so both get the same hook-enter guard. ext/opcache/tests/jit/gh24398.phpt is the issue's reproducer under opcache.jit=1201. It fails without the fix (UBSan error in ZEND_SEND_FUNC_ARG) and passes with it. The other tests in ext/opcache/tests/jit give the same results with and without the change. With the fix, the reproducer also runs cleanly at 1211, 1101, 1251, 1205 and tracing.

As discussed in the issue, this is the level-1 variant of GH-21006 / GH-22857. This PR targets PHP-8.4, where property hooks were introduced.

Fixes #24398.

Found with an LLM-based testing tool; I wrote this fix with help from Claude and reviewed it myself.

Comment thread ext/opcache/jit/zend_jit.c Outdated
call_level--;
}
break;
/* FETCH_OBJ_FUNC_ARG by value dispatches into the FETCH_OBJ_R handler. */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment as-written is inaccurate: it does not always dispatch into FETCH_OBJ_R, it can dispatch into there. Looking at the VM, I wonder if we can avoid the extra code to handle hooks by checking ZEND_CALL_SEND_ARG_BY_REF too.
Also please check if the tracing JIT is affected: see zend_jit_trace_may_exit

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, reworded the comment.

ZEND_CALL_SEND_ARG_BY_REF: the crash is on the by-value path, so the guard is still needed there. A version that checks the flag and guards only the by-value branch also passed gh24398.phpt, but added 42 lines instead of 2.

Tracing was affected too (a named argument, or a 13th argument). The new commit (ff744af) adds FETCH_OBJ_FUNC_ARG to zend_jit_trace_may_exit() with a test (gh24398_002.phpt).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants