Skip to content

Commit 5963656

Browse files
committed
Fix GH-21006: JIT SEGV with FETCH_OBJ_FUNC_ARG and property hooks
When the JIT falls back to the VM handler for FETCH_OBJ_FUNC_ARG (or FETCH_OBJ_R), the handler may find the SIMPLE_GET flag set in the runtime cache for a hooked property. This causes it to push a call frame for the hook function, changing execute_data. When the trace resumes after the handler returns, it continues with the wrong execute_data, leading to a segfault on the next opcode that accesses EX(call). Fix by emitting IR code in zend_jit_trace_handler() to clear the SIMPLE_GET flag in the runtime cache before calling the VM handler, so it falls through to read_property instead.
1 parent f99ca63 commit 5963656

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

ext/opcache/jit/zend_jit_ir.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17091,6 +17091,21 @@ static int zend_jit_trace_handler(zend_jit_ctx *jit, const zend_op_array *op_arr
1709117091
ir_ref ref;
1709217092

1709317093
zend_jit_set_ip(jit, opline);
17094+
/* FETCH_OBJ_FUNC_ARG/FETCH_OBJ_R may dispatch to a VM handler that
17095+
* pushes a call frame for SIMPLE_GET property hooks, which would
17096+
* corrupt the trace's call stack. Clear the SIMPLE_GET flag so the
17097+
* handler falls through to read_property instead. */
17098+
if ((opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG || opline->opcode == ZEND_FETCH_OBJ_R)
17099+
&& opline->op2_type == IS_CONST) {
17100+
ir_ref run_time_cache = ir_LOAD_A(jit_EX(run_time_cache));
17101+
ir_ref cache_slot_ref = ir_ADD_OFFSET(run_time_cache,
17102+
(opline->extended_value & ~ZEND_FETCH_OBJ_FLAGS) + sizeof(void*));
17103+
ir_ref prop_offset_ref = ir_LOAD_A(cache_slot_ref);
17104+
ir_ref if_simple_get = ir_IF(ir_AND_A(prop_offset_ref, ir_CONST_ADDR(ZEND_PROPERTY_HOOK_SIMPLE_GET_BIT)));
17105+
ir_IF_TRUE(if_simple_get);
17106+
ir_STORE(cache_slot_ref, ir_AND_A(prop_offset_ref, ir_CONST_ADDR(~(uintptr_t)ZEND_PROPERTY_HOOK_SIMPLE_GET_BIT)));
17107+
ir_MERGE_WITH_EMPTY_FALSE(if_simple_get);
17108+
}
1709417109
if (GCC_GLOBAL_REGS) {
1709517110
ir_CALL(IR_VOID, ir_CONST_FUNC(handler));
1709617111
} else {

ext/opcache/tests/jit/gh21006.phpt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
--TEST--
2+
GH-21006: JIT SEGV with FETCH_OBJ_FUNC_ARG and property hooks
3+
--INI--
4+
opcache.enable=1
5+
opcache.enable_cli=1
6+
opcache.jit=tracing
7+
opcache.jit_hot_loop=61
8+
opcache.jit_hot_func=127
9+
opcache.jit_hot_return=8
10+
opcache.jit_hot_side_exit=8
11+
--FILE--
12+
<?php
13+
namespace Test;
14+
15+
class C
16+
{
17+
public $prop {
18+
get => 'sha256';
19+
}
20+
21+
public function sign()
22+
{
23+
return hash_hmac(
24+
algo: $this->prop,
25+
data: '',
26+
key: '',
27+
);
28+
}
29+
}
30+
31+
$obj = new C();
32+
for ($i = 0; $i < 100; $i++) {
33+
$obj->sign();
34+
}
35+
echo "OK\n";
36+
?>
37+
--EXPECT--
38+
OK

0 commit comments

Comments
 (0)