From a04a71e551d11b6e23a714966e439507dd0ad504 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Tue, 27 Oct 2026 16:20:37 +0100 Subject: [PATCH] Fix JIT SWITCH_STRING type checks on referenced operand The tracing guard checked IS_LONG instead of IS_STRING after ZVAL_DEREF, passing a non-string to zend_hash_find(). The function JIT path checked IS_STRING instead of IS_REFERENCE, so the deref fast path was dead. --- NEWS | 2 ++ ext/opcache/jit/zend_jit_ir.c | 4 ++-- ext/opcache/tests/jit/switch_002.phpt | 27 +++++++++++++++++++++++++++ ext/opcache/tests/jit/switch_003.phpt | 26 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 ext/opcache/tests/jit/switch_002.phpt create mode 100644 ext/opcache/tests/jit/switch_003.phpt diff --git a/NEWS b/NEWS index ed773193df4a..a9917436431e 100644 --- a/NEWS +++ b/NEWS @@ -141,6 +141,8 @@ PHP NEWS . Fixed bug GH-24438 (Preserve indirect types in JIT side traces). (aasul) . Fixed bug GH-24577 (CE-cache flag on interned class name visible before map_ptr offset). (prateekbhujel) + . Fixed JIT segfault on switch over a string reference holding a + non-string value. (jorgsowa) - PDO: . Fixed invalid ATTR_ORACLE_NULLS values being accepted and truncated. diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index fc38dd5647fd..24ed5c334445 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -16234,7 +16234,7 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_ if (fallback_label) { jit_guard_Z_TYPE(jit, op1_addr, IS_REFERENCE, fallback_label); } else { - ir_ref if_ref = jit_if_Z_TYPE(jit, op1_addr, IS_STRING); + ir_ref if_ref = jit_if_Z_TYPE(jit, op1_addr, IS_REFERENCE); ir_IF_FALSE_cold(if_ref); ir_refs_add(slow_inputs, ir_END()); ir_IF_TRUE(if_ref); @@ -16244,7 +16244,7 @@ static int zend_jit_switch(zend_jit_ctx *jit, const zend_op *opline, const zend_ op1_addr = ZEND_ADDR_REF_ZVAL(ref2); if (fallback_label) { - jit_guard_Z_TYPE(jit, op1_addr, IS_LONG, fallback_label); + jit_guard_Z_TYPE(jit, op1_addr, IS_STRING, fallback_label); } else { if_string = jit_if_Z_TYPE(jit, op1_addr, IS_STRING); ir_IF_FALSE_cold(if_string); diff --git a/ext/opcache/tests/jit/switch_002.phpt b/ext/opcache/tests/jit/switch_002.phpt new file mode 100644 index 000000000000..580a1134c22e --- /dev/null +++ b/ext/opcache/tests/jit/switch_002.phpt @@ -0,0 +1,27 @@ +--TEST-- +JIT SWITCH_STRING on a reference holding a non-string value +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=tracing +opcache.jit_hot_loop=1 +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +int(1) +int(0) diff --git a/ext/opcache/tests/jit/switch_003.phpt b/ext/opcache/tests/jit/switch_003.phpt new file mode 100644 index 000000000000..2a33f6dac457 --- /dev/null +++ b/ext/opcache/tests/jit/switch_003.phpt @@ -0,0 +1,26 @@ +--TEST-- +JIT SWITCH_STRING on a reference (function JIT) +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.file_update_protection=0 +opcache.jit=function +--EXTENSIONS-- +opcache +--FILE-- + +--EXPECT-- +int(2) +int(0)