diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 2cc6a01c4107..76cf8f073f30 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14116,6 +14116,26 @@ static int zend_jit_class_guard(zend_jit_ctx *jit, const zend_op *opline, ir_ref return 1; } +static int zend_jit_func_arg_by_ref_guard(zend_jit_ctx *jit, const zend_op *opline) +{ + int32_t exit_point = zend_jit_trace_get_exit_point(opline, ZEND_JIT_EXIT_TO_VM); + const void *exit_addr = zend_jit_trace_get_exit_addr(exit_point); + ir_ref rx, call_info; + + if (!exit_addr) { + return 0; + } + if (jit->reuse_ip) { + rx = jit_IP(jit); + } else { + rx = ir_LOAD_A(jit_EX(call)); + } + call_info = ir_LOAD_U32(jit_CALL(rx, This.u1.type_info)); + ir_GUARD_NOT(ir_AND_U32(call_info, ir_CONST_U32(ZEND_CALL_SEND_ARG_BY_REF)), + ir_CONST_ADDR(exit_addr)); + return 1; +} + static int zend_jit_fetch_obj(zend_jit_ctx *jit, const zend_op *opline, const zend_op_array *op_array, diff --git a/ext/opcache/jit/zend_jit_trace.c b/ext/opcache/jit/zend_jit_trace.c index da97d102f202..93d3af997f18 100644 --- a/ext/opcache/jit/zend_jit_trace.c +++ b/ext/opcache/jit/zend_jit_trace.c @@ -5989,9 +5989,14 @@ static const void *zend_jit_trace(zend_jit_trace_rec *trace_buffer, uint32_t par if (!JIT_G(current_frame) || !JIT_G(current_frame)->call || !JIT_G(current_frame)->call->func - || !TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { + || TRACE_FRAME_IS_LAST_SEND_BY_REF(JIT_G(current_frame)->call)) { break; } + if (!TRACE_FRAME_IS_LAST_SEND_BY_VAL(JIT_G(current_frame)->call)) { + if (!zend_jit_func_arg_by_ref_guard(&ctx, opline)) { + goto jit_failure; + } + } ZEND_FALLTHROUGH; case ZEND_FETCH_OBJ_R: case ZEND_FETCH_OBJ_IS: diff --git a/ext/opcache/jit/zend_jit_vm_helpers.c b/ext/opcache/jit/zend_jit_vm_helpers.c index 8b85324ef897..eed7acde7888 100644 --- a/ext/opcache/jit/zend_jit_vm_helpers.c +++ b/ext/opcache/jit/zend_jit_vm_helpers.c @@ -859,6 +859,7 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, } } break; + case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_OBJ_R: { if (opline->op2_type == IS_CONST) { /* Remove the SIMPLE_GET flag to avoid inlining hooks. */ @@ -873,7 +874,6 @@ zend_jit_trace_stop ZEND_FASTCALL zend_jit_trace_execute(zend_execute_data *ex, case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_RW: case ZEND_FETCH_OBJ_IS: - case ZEND_FETCH_OBJ_FUNC_ARG: case ZEND_FETCH_OBJ_UNSET: case ZEND_ASSIGN_OBJ: case ZEND_ASSIGN_OBJ_OP: diff --git a/ext/opcache/tests/jit/gh21006.phpt b/ext/opcache/tests/jit/gh21006.phpt new file mode 100644 index 000000000000..a79c14efc99d --- /dev/null +++ b/ext/opcache/tests/jit/gh21006.phpt @@ -0,0 +1,57 @@ +--TEST-- +GH-21006: JIT SEGV with FETCH_OBJ_FUNC_ARG and property hooks +--INI-- +opcache.enable=1 +opcache.enable_cli=1 +opcache.jit=tracing +opcache.jit_hot_loop=1 +opcache.jit_hot_func=1 +opcache.jit_hot_return=1 +opcache.jit_hot_side_exit=1 +--FILE-- + 'sha256'; + } + + public function sign() + { + return hash_hmac( + algo: $this->prop, + data: '', + key: '', + ); + } +} + +$obj = new C(); +for ($i = 0; $i < 100; $i++) { + $obj->sign(); +} + +#[\AllowDynamicProperties] +class D +{ + public function test() + { + return hash_hmac( + algo: $this->algo, + data: '', + key: '', + ); + } +} + +$d = new D(); +$d->algo = 'sha256'; +for ($i = 0; $i < 100; $i++) { + $d->test(); +} +echo "OK\n"; +?> +--EXPECT-- +OK