Commit 616e11d3d42 for php.net

commit 616e11d3d4202cc0184ce9207b136fd55c85741e
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date:   Mon Jul 27 06:40:52 2026 -0400

    Fix GH-18847: save EX(opline) before extending the VM stack on all VMs (#22542)

    db7193f31ea9 sets EX(opline) before zend_vm_stack_extend() so that a fatal
    error raised while a call frame is pushed can walk the backtrace, but the
    wrappers doing it were compiled only for global-register CALL/HYBRID
    builds. Elsewhere the save is skipped, and a frame entered by the tracing
    JIT still holds a NULL opline when its first INIT_FCALL hits the memory
    limit. Thread execute_data and opline into the wrappers so that every VM
    kind performs the save.

    Fixes GH-18847
    Closes GH-22542

diff --git a/NEWS b/NEWS
index 91ac10efd3b..cc2eed87ccf 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,9 @@ PHP                                                                        NEWS
     failure path). (David Carlier)
   . Fixed bug GH-18985 (Wrong line numbers for match with constant arms).
     (ilutov)
+  . Fixed bug GH-18847 (SEGV in zend_fetch_debug_backtrace() when the memory
+    limit is reached while the tracing JIT enters a call frame). (Arnaud,
+    iliaal)

 - DOM:
   . Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD
diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c
index f5b70d74aa6..91cab1b63d2 100644
--- a/Zend/zend_execute.c
+++ b/Zend/zend_execute.c
@@ -5729,9 +5729,8 @@ ZEND_API void ZEND_FASTCALL zend_free_extra_named_params(zend_array *extra_named
 	zend_array_release(extra_named_params);
 }

-#if defined(ZEND_VM_IP_GLOBAL_REG) && ((ZEND_VM_KIND == ZEND_VM_KIND_CALL) || (ZEND_VM_KIND == ZEND_VM_KIND_HYBRID))
 /* Special versions of functions that sets EX(opline) before calling zend_vm_stack_extend() */
-static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope) /* {{{ */
+static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(uint32_t used_stack, uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope EXECUTE_DATA_DC OPLINE_DC) /* {{{ */
 {
 	zend_execute_data *call = (zend_execute_data*)EG(vm_stack_top);

@@ -5750,17 +5749,13 @@ static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame_ex(u
 	}
 } /* }}} */

-static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope) /* {{{ */
+static zend_always_inline zend_execute_data *_zend_vm_stack_push_call_frame(uint32_t call_info, zend_function *func, uint32_t num_args, void *object_or_called_scope EXECUTE_DATA_DC OPLINE_DC) /* {{{ */
 {
 	uint32_t used_stack = zend_vm_calc_used_stack(num_args, func);

 	return _zend_vm_stack_push_call_frame_ex(used_stack, call_info,
-		func, num_args, object_or_called_scope);
+		func, num_args, object_or_called_scope EXECUTE_DATA_CC OPLINE_CC);
 } /* }}} */
-#else
-# define _zend_vm_stack_push_call_frame_ex zend_vm_stack_push_call_frame_ex
-# define _zend_vm_stack_push_call_frame    zend_vm_stack_push_call_frame
-#endif

 #ifdef ZEND_VM_TRACE_HANDLERS
 # include "zend_vm_trace_handlers.h"
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 0e5efa6f826..cd74145799b 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -3912,7 +3912,7 @@ ZEND_VM_HOT_HANDLER(59, ZEND_INIT_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT)
 		CACHE_PTR(opline->result.num, fbc);
 	}
 	call = _zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
-		fbc, opline->extended_value, NULL);
+		fbc, opline->extended_value, NULL EXECUTE_DATA_CC OPLINE_CC);
 	call->prev_execute_data = EX(call);
 	EX(call) = call;

@@ -4070,7 +4070,7 @@ ZEND_VM_HOT_HANDLER(69, ZEND_INIT_NS_FCALL_BY_NAME, ANY, CONST, NUM|CACHE_SLOT)
 	}

 	call = _zend_vm_stack_push_call_frame(ZEND_CALL_NESTED_FUNCTION,
-		fbc, opline->extended_value, NULL);
+		fbc, opline->extended_value, NULL EXECUTE_DATA_CC OPLINE_CC);
 	call->prev_execute_data = EX(call);
 	EX(call) = call;

@@ -4099,7 +4099,7 @@ ZEND_VM_HOT_HANDLER(61, ZEND_INIT_FCALL, NUM, CONST, NUM|CACHE_SLOT)

 	call = _zend_vm_stack_push_call_frame_ex(
 		opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
-		fbc, opline->extended_value, NULL);
+		fbc, opline->extended_value, NULL EXECUTE_DATA_CC OPLINE_CC);
 	call->prev_execute_data = EX(call);
 	EX(call) = call;

@@ -4118,7 +4118,7 @@ ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_INIT_FCALL, Z_EXTRA_P(RT_CONSTANT(op, op->op2
 	}
 	call = _zend_vm_stack_push_call_frame_ex(
 		opline->op1.num, ZEND_CALL_NESTED_FUNCTION,
-		fbc, opline->extended_value, NULL);
+		fbc, opline->extended_value, NULL EXECUTE_DATA_CC OPLINE_CC);
 	call->prev_execute_data = EX(call);
 	EX(call) = call;
 	ZEND_VM_NEXT_OPCODE();
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 6b675837d3a..8d2e0670ee3 100644
Binary files a/Zend/zend_vm_execute.h and b/Zend/zend_vm_execute.h differ
diff --git a/ext/opcache/tests/jit/gh18847.phpt b/ext/opcache/tests/jit/gh18847.phpt
new file mode 100644
index 00000000000..b6dd5a7d658
--- /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--
+<?php
+if (getenv("SKIP_SLOW_TESTS")) die("skip slow test");
+?>
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.jit=tracing
+opcache.jit_buffer_size=8M
+fatal_error_backtraces=1
+memory_limit=8M
+--FILE--
+<?php
+function f() {
+    static $x = f();
+}
+f();
+?>
+--EXPECTREGEX--
+Fatal error: Allowed memory size of \d+ bytes exhausted.*#\d+ \{main\}