diff --git a/Zend/Optimizer/zend_inference.c b/Zend/Optimizer/zend_inference.c index 7169238893159..262563aa84c74 100644 --- a/Zend/Optimizer/zend_inference.c +++ b/Zend/Optimizer/zend_inference.c @@ -3767,6 +3767,36 @@ static zend_always_inline zend_result _zend_update_type_info( case ZEND_FETCH_OBJ_W: case ZEND_FETCH_OBJ_UNSET: case ZEND_FETCH_OBJ_FUNC_ARG: + if (ssa_op->op1_def >= 0) { + tmp = t1; + if (tmp & (MAY_BE_RC1|MAY_BE_RCN)) { + do { + const zend_class_entry *ce = NULL; + + if (opline->op1_type == IS_UNUSED) { + ce = op_array->scope; + } else if (ssa_op->op1_use >= 0 && !ssa->var_info[ssa_op->op1_use].is_instanceof) { + ce = ssa->var_info[ssa_op->op1_use].ce; + } + + // FIXME: Could be more permissive by checking for corresponding bp type, + // handler, hook and magic method. + if (ce + && !ce->create_object + && ce->default_object_handlers == &std_object_handlers) { + const zend_property_info *prop_info = zend_fetch_prop_info(op_array, ssa, opline, ssa_op); + if ((prop_info && !prop_info->hooks) + || (!ce->__get && !ce->__set && !ce->__isset && !ce->__unset)) { + break; + } + } + + tmp |= (MAY_BE_RC1|MAY_BE_RCN); + } while (0); + } + UPDATE_SSA_TYPE(tmp, ssa_op->op1_def); + COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->op1_def); + } if (ssa_op->result_def >= 0) { uint32_t tmp = 0; ce = NULL; diff --git a/Zend/Optimizer/zend_ssa.c b/Zend/Optimizer/zend_ssa.c index 4c09e5e105b50..79390b7a2addc 100644 --- a/Zend/Optimizer/zend_ssa.c +++ b/Zend/Optimizer/zend_ssa.c @@ -794,7 +794,18 @@ static zend_always_inline int _zend_ssa_rename_op(const zend_op_array *op_array, } } } + break; } + case ZEND_FETCH_OBJ_R: + case ZEND_FETCH_OBJ_IS: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_FETCH_OBJ_FUNC_ARG: + if ((build_flags & ZEND_SSA_RC_INFERENCE) && (opline->op1_type & (IS_CV|IS_VAR|IS_TMP_VAR))) { + goto add_op1_def; + } + break; default: break; } diff --git a/ext/opcache/jit/zend_jit_ir.c b/ext/opcache/jit/zend_jit_ir.c index 6eb5d4c7b4e5a..f07fa8242d1d4 100644 --- a/ext/opcache/jit/zend_jit_ir.c +++ b/ext/opcache/jit/zend_jit_ir.c @@ -14591,6 +14591,9 @@ static int zend_jit_fetch_obj(zend_jit_ctx *jit, if (on_this) { op1_info &= ~MAY_BE_RC1; } + if (ssa_op->op1_def >= 0) { + op1_info |= (ssa->var_info[ssa_op->op1_def].type & (MAY_BE_RC1|MAY_BE_RCN)); + } jit_FREE_OP(jit, opline->op1_type, opline->op1, op1_info, opline); } } diff --git a/ext/opcache/tests/jit/gh17151_1.phpt b/ext/opcache/tests/jit/gh17151_1.phpt new file mode 100644 index 0000000000000..57c9bd142b72f --- /dev/null +++ b/ext/opcache/tests/jit/gh17151_1.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-17151: ZEND_FETCH_OBJ_R may modify RC of op1 +--FILE-- +bar; + var_dump($x); +} + +test(); + +?> +--EXPECTF-- +object(C)#%d (0) { +} diff --git a/ext/opcache/tests/jit/gh17151_2.phpt b/ext/opcache/tests/jit/gh17151_2.phpt new file mode 100644 index 0000000000000..26e1acbab7d9e --- /dev/null +++ b/ext/opcache/tests/jit/gh17151_2.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-17151: ZEND_FETCH_OBJ_R may modify RC of op1 +--FILE-- +bar; +} + +test(); +echo "Done\n"; + +?> +--EXPECT-- +C::__destruct +Done