From 1db1c7f5c1b4148e8de2e403ed6a582e3167c758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chris=20Hasi=C5=84ski?= Date: Sat, 17 Jan 2026 00:25:44 +0100 Subject: [PATCH] Fix segfault in Tracing JIT with object reference (GH-20818) When FE_RESET_RW executes, it converts the CV to a reference before checking if the array/object is empty. However, when the JIT creates exit points for FE_RESET_RW in zend_jit_trace_handler(), it wasn't updating the stack type for op1 to reflect this change. This caused side traces compiled from these exit points to have incorrect type information. The side trace's CV cleanup code would see IS_OBJECT and generate a direct call to zend_objects_store_del(), but the actual value was a zend_reference*, causing a segfault. The fix adds ZEND_FE_RESET_RW to the list of opcodes that temporarily set their op1 stack type to IS_UNKNOWN before creating exit points. This follows the same pattern used for ZEND_BIND_INIT_STATIC_OR_JMP. When IS_UNKNOWN, the JIT falls back to SSA type info which correctly includes MAY_BE_REF for FE_RESET_RW's op1_def. Fixes GH-20818 Closes GH-20948 --- NEWS | 4 ++++ ext/opcache/jit/zend_jit_ir.c | 2 ++ ext/opcache/tests/jit/gh20818.phpt | 30 ++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 ext/opcache/tests/jit/gh20818.phpt diff --git a/NEWS b/NEWS index 80b69c11cf8..7d90a01f51d 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,10 @@ PHP NEWS . Fixed bug GH-20836 (Stack overflow in mb_convert_variables with recursive array references). (alexandre-daubois) +- Opcache: + . Fixed bug GH-20818 (Segfault in Tracing JIT with object reference). + (khasinski) + - Phar: . Fixed bug GH-20882 (buildFromIterator breaks with missing base directory). (ndossche) diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 4f25678e114..3ddaa027088 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16938,6 +16938,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr SET_STACK_TYPE(stack, EX_VAR_TO_NUM(opline->op2.var), IS_UNKNOWN, 1); } break; + case ZEND_FE_RESET_RW: case ZEND_BIND_INIT_STATIC_OR_JMP: if (opline->op1_type == IS_CV) { old_info = STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var)); @@ -16962,6 +16963,7 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op2.var), old_info); } break; + case ZEND_FE_RESET_RW: case ZEND_BIND_INIT_STATIC_OR_JMP: if (opline->op1_type == IS_CV) { SET_STACK_INFO(stack, EX_VAR_TO_NUM(opline->op1.var), old_info); diff --git a/ext/opcache/tests/jit/gh20818.phpt b/ext/opcache/tests/jit/gh20818.phpt new file mode 100644 index 00000000000..9423856b66b --- /dev/null +++ b/ext/opcache/tests/jit/gh20818.phpt @@ -0,0 +1,30 @@ +--TEST-- +GH-20818 (Segfault in Tracing JIT with Object Reference) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_buffer_size=1M +--FILE-- + 1], + (object) ["" => 1], + (object) [], +]; + +for ($i = 0; $i < 200; $i += 1) { + foreach ($data as $entry) { + process($entry); + } +} + +echo "Done\n"; +?> +--EXPECT-- +Done