From 7b4798f8a3319085b49a69b371a424d6aefc0ff8 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 1 Jul 2026 14:19:39 -0400 Subject: [PATCH] Fix GH-18847: set EX(opline) when the tracing JIT enters a call frame The tracing JIT enters a callee frame without writing EX(opline) to memory; it keeps the opline in the IP register and writes it back lazily. When a fatal error is raised before that first store, such as an out-of-memory while the callee's first opcode pushes its own call frame, zend_fetch_debug_backtrace() dereferences the NULL EX(opline). Materialize it at entry, as the observer path already did behind ZEND_OBSERVER_ENABLED. Fixes GH-18847 --- ext/opcache/jit/zend_jit_ir.c | 7 +++++++ ext/opcache/tests/jit/gh18847.phpt | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 ext/opcache/tests/jit/gh18847.phpt diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index d62ef95b5513..55aef3b4f267 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -10436,6 +10436,13 @@ static int zend_jit_do_fcall(zend_jit_ctx *jit, const zend_op *opline, const zen ir_MERGE_WITH_EMPTY_FALSE(if_need); } + if (trace && (trace->op != ZEND_JIT_TRACE_END || trace->stop < ZEND_JIT_TRACE_STOP_INTERPRETER)) { + ZEND_ASSERT(trace[1].op == ZEND_JIT_TRACE_VM || trace[1].op == ZEND_JIT_TRACE_END); + ir_STORE(jit_EX(opline), ir_CONST_ADDR(trace[1].opline)); + } else { + ir_STORE(jit_EX(opline), jit_IP(jit)); + } + if (ZEND_OBSERVER_ENABLED && (!func || (func->common.fn_flags & (ZEND_ACC_CALL_VIA_TRAMPOLINE | ZEND_ACC_GENERATOR)) == 0)) { ir_ref observer_handler; ir_ref rx = jit_FP(jit); diff --git a/ext/opcache/tests/jit/gh18847.phpt b/ext/opcache/tests/jit/gh18847.phpt new file mode 100644 index 000000000000..b6dd5a7d6587 --- /dev/null +++ b/ext/opcache/tests/jit/gh18847.phpt @@ -0,0 +1,24 @@ +--TEST-- +GH-18847 (SEGV in zend_fetch_debug_backtrace when the tracing JIT enters a frame and the memory limit is hit before opline is set) +--EXTENSIONS-- +opcache +--SKIPIF-- + +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_buffer_size=8M +fatal_error_backtraces=1 +memory_limit=8M +--FILE-- + +--EXPECTREGEX-- +Fatal error: Allowed memory size of \d+ bytes exhausted.*#\d+ \{main\}