From 6b855af572cbaf9619e89fbb629f4114faaae918 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Tue, 12 May 2026 15:47:27 -0400 Subject: [PATCH] Fix GH-21368 follow-up: escape_if_undef on CALL VM Two issues reachable on debug Clang/CALL builds of PHP-8.4. ir_CONST_ADDR for orig_handler trips jit_CONST_FUNC_PROTO's FUNC_ADDR assertion when another site reuses the same handler via ir_CONST_FC_FUNC in the same trace compile (Symfony HttpClient test suite repro). The ir_TAILCALL_1 to orig_handler leaves execute_ex with stale execute_data after a frame-changing handler. PHP-8.4's CALL VM needs a non-zero return to reload from EG(current_execute_data); the tail-call discards that signal. gh21267.phpt with aggressive jit_hot_* ini reproduces a crash in ZEND_DO_FCALL. Use ir_CONST_FC_FUNC to register the handler as FUNC_ADDR, and mirror zend_jit_tail_handler's ZEND_FUNC_RECURSIVE_DIRECTLY branch (ir_CALL_1 + ir_RETURN(1)) so execute_ex reloads. The ir_CAST_FC_FUNC on x86 becomes dead. --- ext/opcache/jit/zend_jit_ir.c | 8 ++--- ext/opcache/tests/jit/gh21368_call_vm.phpt | 36 ++++++++++++++++++++++ 2 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 ext/opcache/tests/jit/gh21368_call_vm.phpt diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 1346d141754f..f99605ee4208 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -8096,14 +8096,12 @@ static int zend_jit_escape_if_undef(zend_jit_ctx *jit, int var, uint32_t flags, zend_jit_op_array_trace_extension *jit_extension = (zend_jit_op_array_trace_extension*)ZEND_FUNC_INFO(op_array); size_t offset = jit_extension->offset; - ir_ref ref = ir_CONST_ADDR(ZEND_OP_TRACE_INFO((opline - 1), offset)->orig_handler); + ir_ref ref = ir_CONST_FC_FUNC(ZEND_OP_TRACE_INFO((opline - 1), offset)->orig_handler); if (GCC_GLOBAL_REGS) { ir_TAILCALL(IR_VOID, ref); } else { -#if defined(IR_TARGET_X86) - ref = ir_CAST_FC_FUNC(ref); -#endif - ir_TAILCALL_1(IR_I32, ref, jit_FP(jit)); + ir_CALL_1(IR_I32, ref, jit_FP(jit)); + ir_RETURN(ir_CONST_I32(1)); } ir_IF_TRUE(if_def); diff --git a/ext/opcache/tests/jit/gh21368_call_vm.phpt b/ext/opcache/tests/jit/gh21368_call_vm.phpt new file mode 100644 index 000000000000..cc64e62a610e --- /dev/null +++ b/ext/opcache/tests/jit/gh21368_call_vm.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-21368 (JIT escape_if_undef SEGV on CALL VM with aggressive trace counters) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit_buffer_size=64M +opcache.jit=tracing +opcache.protect_memory=1 +opcache.jit_hot_loop=1 +opcache.jit_hot_func=1 +opcache.jit_hot_return=1 +opcache.jit_hot_side_exit=1 +--FILE-- +x; } +} + +$o1 = new C; +$o2 = new C; +$o2->x = false; +$o3 = new C; +unset($o3->x); +$a = [$o1, $o2, $o3]; + +for ($i = 0; $i < 8; $i++) { + $m = $a[$i % 3]; + $m->getX(); + $m->getX(); +} +?> +OK +--EXPECT-- +OK