diff --git a/NEWS b/NEWS index 5ea9ecbafb0b..72d7d5158452 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,8 @@ PHP NEWS for grouping. (Klaas Skelte van der Werf) . Fixed bug GH-24265 (Unwind/GracefulExit discarded in zend_fiber_execute()). (David Carlier) + . Fixed bug GH-24712 (Use-after-free in foreach by reference when destructor + modifies the loop variable). (Lazizbek Ergashev) - Date: . Fixed bug GH-20503 (Assertion failure with ext/date DateInterval property diff --git a/Zend/tests/gh24712.phpt b/Zend/tests/gh24712.phpt new file mode 100644 index 000000000000..9206b9bb975c --- /dev/null +++ b/Zend/tests/gh24712.phpt @@ -0,0 +1,15 @@ +--TEST-- +GH-24712 (Use-after-free in foreach by reference when destructor modifies the loop variable) +--FILE-- + +--EXPECT-- +NULL diff --git a/Zend/tests/gh24712_2.phpt b/Zend/tests/gh24712_2.phpt new file mode 100644 index 000000000000..bc76f17c7711 --- /dev/null +++ b/Zend/tests/gh24712_2.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-24712 (Use-after-free in static variable binding when destructor modifies the variable) +--FILE-- +var] = null; + } +} +function init() { + return 1; +} +$a = new C('a'); +static $a; +var_dump($a); + +for ($i = 0; $i < 2; $i++) { + unset($b); + $b = $i ? new C('b') : 0; + static $b = init(); +} +var_dump($b); +?> +--EXPECT-- +NULL +NULL diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index df255c5a868f..56dbd802c8ae 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -7402,11 +7402,13 @@ ZEND_VM_C_LABEL(fe_fetch_w_exit): zval *variable_ptr = EX_VAR(opline->op2.var); if (EXPECTED(variable_ptr != value)) { zend_reference *ref; + zval garbage; ref = Z_REF_P(value); GC_ADDREF(ref); - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, ref); + i_zval_ptr_dtor(&garbage); } } else { Z_ADDREF_P(value); @@ -9056,6 +9058,7 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, ANY, REF) HashTable *ht; zval *value; zval *variable_ptr; + zval garbage; variable_ptr = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_W); @@ -9085,19 +9088,22 @@ ZEND_VM_HANDLER(183, ZEND_BIND_STATIC, CV, ANY, REF) ref->sources.ptr = NULL; Z_REF_P(value) = ref; Z_TYPE_INFO_P(value) = IS_REFERENCE_EX; - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, ref); + i_zval_ptr_dtor(&garbage); } else { Z_ADDREF_P(value); - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, Z_REF_P(value)); + i_zval_ptr_dtor(&garbage); if (OP2_TYPE != IS_UNUSED) { FREE_OP2(); } } } else { - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_COPY(variable_ptr, value); + i_zval_ptr_dtor(&garbage); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -9122,11 +9128,14 @@ ZEND_VM_HANDLER(203, ZEND_BIND_INIT_STATIC_OR_JMP, CV, JMP_ADDR) if (Z_TYPE_P(value) == IS_NULL) { ZEND_VM_NEXT_OPCODE(); } else { + zval garbage; + SAVE_OPLINE(); - zval_ptr_dtor(variable_ptr); ZEND_ASSERT(Z_TYPE_P(value) == IS_REFERENCE); Z_ADDREF_P(value); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, Z_REF_P(value)); + zval_ptr_dtor(&garbage); ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 1); } } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 4b313f20cd39..00f94f0ce8a2 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -23317,11 +23317,13 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_FETCH_RW_SPEC_VAR_HANDLER(Z zval *variable_ptr = EX_VAR(opline->op2.var); if (EXPECTED(variable_ptr != value)) { zend_reference *ref; + zval garbage; ref = Z_REF_P(value); GC_ADDREF(ref); - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, ref); + i_zval_ptr_dtor(&garbage); } } else { Z_ADDREF_P(value); @@ -41856,6 +41858,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_HANDLER(ZE HashTable *ht; zval *value; zval *variable_ptr; + zval garbage; variable_ptr = EX_VAR(opline->op1.var); @@ -41885,19 +41888,22 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_STATIC_SPEC_CV_HANDLER(ZE ref->sources.ptr = NULL; Z_REF_P(value) = ref; Z_TYPE_INFO_P(value) = IS_REFERENCE_EX; - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, ref); + i_zval_ptr_dtor(&garbage); } else { Z_ADDREF_P(value); - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, Z_REF_P(value)); + i_zval_ptr_dtor(&garbage); if (opline->op2_type != IS_UNUSED) { FREE_OP(opline->op2_type, opline->op2.var); } } } else { - i_zval_ptr_dtor(variable_ptr); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_COPY(variable_ptr, value); + i_zval_ptr_dtor(&garbage); } ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -41922,11 +41928,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_BIND_INIT_STATIC_OR_JMP_SPEC_C if (Z_TYPE_P(value) == IS_NULL) { ZEND_VM_NEXT_OPCODE(); } else { + zval garbage; + SAVE_OPLINE(); - zval_ptr_dtor(variable_ptr); ZEND_ASSERT(Z_TYPE_P(value) == IS_REFERENCE); Z_ADDREF_P(value); + ZVAL_COPY_VALUE(&garbage, variable_ptr); ZVAL_REF(variable_ptr, Z_REF_P(value)); + zval_ptr_dtor(&garbage); ZEND_VM_JMP_EX(OP_JMP_ADDR(opline, opline->op2), 1); } }