From e5e8392c23627da996b2fb94e01e0687b52516e9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 28 Oct 2025 18:26:10 +0100 Subject: [PATCH] Prevent ASSIGN_OP / ASSIGN_DIM_OP lvalue from being released during assignment In ASSIGN_DIM_OP, side effects of coercion may release the array or make the dimension pointer invalid (by reallocating the array). Increasing the array's refcount around the binary op is enough to prevent both issues. In ASSIGN_OP, if the variable is a reference, side effects may release it. Again, increasing the refcount prevents this. --- Zend/tests/gh20319-001.phpt | 32 +++ Zend/tests/gh20319-002.phpt | 33 +++ Zend/tests/gh20319-003.phpt | 36 +++ Zend/tests/gh20319-004.phpt | 41 +++ Zend/tests/gh20319-005.phpt | 35 +++ Zend/zend_vm_def.h | 44 ++- Zend/zend_vm_execute.h | 560 +++++++++++++++++++++++++++++------- 7 files changed, 677 insertions(+), 104 deletions(-) create mode 100644 Zend/tests/gh20319-001.phpt create mode 100644 Zend/tests/gh20319-002.phpt create mode 100644 Zend/tests/gh20319-003.phpt create mode 100644 Zend/tests/gh20319-004.phpt create mode 100644 Zend/tests/gh20319-005.phpt diff --git a/Zend/tests/gh20319-001.phpt b/Zend/tests/gh20319-001.phpt new file mode 100644 index 0000000000000..8f68258e700b5 --- /dev/null +++ b/Zend/tests/gh20319-001.phpt @@ -0,0 +1,32 @@ +--TEST-- +GH-20319 001: ASSIGN_OP: Ref lvalue may be released by __toString() +--ENV-- +LEN=10 +--FILE-- + +--EXPECT-- +string(10) "dddddddddd" +bool(false) +bool(false) diff --git a/Zend/tests/gh20319-002.phpt b/Zend/tests/gh20319-002.phpt new file mode 100644 index 0000000000000..35445fb66b26a --- /dev/null +++ b/Zend/tests/gh20319-002.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-20319 002: ASSIGN_OP: var-var ref lvalue may be released by __toString() +--ENV-- +LEN=10 +--FILE-- + +--EXPECT-- +string(10) "dddddddddd" +bool(false) +bool(false) diff --git a/Zend/tests/gh20319-003.phpt b/Zend/tests/gh20319-003.phpt new file mode 100644 index 0000000000000..a1b6811054b51 --- /dev/null +++ b/Zend/tests/gh20319-003.phpt @@ -0,0 +1,36 @@ +--TEST-- +GH-20319 003: ASSIGN_DIM_OP: Array lvalue may be released by __toString() +--ENV-- +LEN=10 +--FILE-- + +--EXPECT-- +string(14) "testdddddddddd" +bool(false) +bool(false) diff --git a/Zend/tests/gh20319-004.phpt b/Zend/tests/gh20319-004.phpt new file mode 100644 index 0000000000000..ab3e7f571ef4d --- /dev/null +++ b/Zend/tests/gh20319-004.phpt @@ -0,0 +1,41 @@ +--TEST-- +GH-20319 004: ASSIGN_DIM_OP: Array dim lvalue may be released by __toString() +--FILE-- + +--EXPECT-- +string(4) "test" +array(0) { +} +array(0) { +} diff --git a/Zend/tests/gh20319-005.phpt b/Zend/tests/gh20319-005.phpt new file mode 100644 index 0000000000000..1c7166f9c3382 --- /dev/null +++ b/Zend/tests/gh20319-005.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-20319 005: ASSIGN_OP: var-var slot may be invalidated by __toString() +--ENV-- +LEN=10 +--FILE-- + +--EXPECT-- +string(14) "testaaaaaaaaaa" +string(14) "testaaaaaaaaaa" diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 34e58639c64cd..1f491bc384a6e 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1188,6 +1188,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (OP2_TYPE != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -1203,6 +1206,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -1264,22 +1270,44 @@ ZEND_VM_HANDLER(26, ZEND_ASSIGN_OP, VAR|CV, CONST|TMPVAR|CV, OP) value = GET_OP2_ZVAL_PTR(BP_VAR_R); var_ptr = GET_OP1_ZVAL_PTR_PTR(BP_VAR_RW); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +ZEND_VM_C_LABEL(assign_op_ref):; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + ZEND_VM_C_GOTO(assign_op_end); + } + } else { + if (OP1_TYPE == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + ZEND_VM_C_GOTO(assign_op_ref); } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +ZEND_VM_C_LABEL(assign_op_end): FREE_OP2(); FREE_OP1(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index cc8bc51a7f4ea..69f374d3852f3 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -24124,6 +24124,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -24139,6 +24142,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -24200,22 +24206,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = RT_CONSTANT(opline, opline->op2); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -27191,6 +27220,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -27206,6 +27238,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -27267,22 +27302,44 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -29687,6 +29744,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -29702,6 +29762,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -31601,6 +31664,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -31616,6 +31682,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -31677,22 +31746,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -43532,6 +43624,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -43547,6 +43642,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -43609,22 +43707,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = RT_CONSTANT(opline, opline->op2); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + @@ -47609,6 +47730,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -47624,6 +47748,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -47686,22 +47813,44 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -50785,6 +50934,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -50800,6 +50952,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -53241,6 +53396,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -53256,6 +53414,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_DIM_OP if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -53318,22 +53479,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_FUNC_CCONV ZEND_ASSIGN_OP_SPE value = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + @@ -79366,6 +79550,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -79381,6 +79568,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -79442,22 +79632,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_VAR value = RT_CONSTANT(opline, opline->op2); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -82433,6 +82646,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -82448,6 +82664,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -82509,22 +82728,44 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_VAR value = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -84929,6 +85170,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -84944,6 +85188,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -86843,6 +87090,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -86858,6 +87108,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -86919,22 +87172,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_VAR value = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_VAR == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + zval_ptr_dtor_nogc(EX_VAR(opline->op1.var)); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -98774,6 +99050,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -98789,6 +99068,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -98851,22 +99133,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_CV_ value = RT_CONSTANT(opline, opline->op2); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: + @@ -102851,6 +103156,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -102866,6 +103174,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -102928,22 +103239,44 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_CV_ value = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: zval_ptr_dtor_nogc(EX_VAR(opline->op2.var)); @@ -106027,6 +106360,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -106042,6 +106378,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -108381,6 +108720,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + /* Prevents array from being released or updated during binary_op */ + GC_ADDREF(ht); + do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -108396,6 +108738,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_DIM_OP_SPEC if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } + + GC_DTOR(ht); + FREE_OP((opline+1)->op1_type, (opline+1)->op1.var); } else { if (EXPECTED(Z_ISREF_P(container))) { @@ -108458,22 +108803,45 @@ static ZEND_OPCODE_HANDLER_RET ZEND_OPCODE_HANDLER_CCONV ZEND_ASSIGN_OP_SPEC_CV_ value = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); var_ptr = _get_zval_ptr_cv_BP_VAR_RW(opline->op1.var EXECUTE_DATA_CC); - do { - if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { - zend_reference *ref = Z_REF_P(var_ptr); - var_ptr = Z_REFVAL_P(var_ptr); - if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { - zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); - break; + if (UNEXPECTED(Z_TYPE_P(var_ptr) == IS_REFERENCE)) { +assign_op_ref:; + zend_reference *ref = Z_REF_P(var_ptr); + GC_ADDREF(ref); + + var_ptr = Z_REFVAL_P(var_ptr); + if (UNEXPECTED(ZEND_REF_HAS_TYPE_SOURCES(ref))) { + zend_binary_assign_op_typed_ref(ref, value OPLINE_CC EXECUTE_DATA_CC); + } else { + zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + } + + if (UNEXPECTED(GC_DELREF(ref) == 0)) { + if (RETURN_VALUE_USED(opline)) { + ZVAL_COPY_VALUE(EX_VAR(opline->result.var), var_ptr); + } else { + zval_ptr_dtor(var_ptr); } + efree_size(ref, sizeof(zend_reference)); + goto assign_op_end; + } + } else { + if (IS_CV == IS_VAR) { + ZEND_ASSERT(Z_TYPE_P(EX_VAR(opline->op1.num)) == IS_INDIRECT); + /* op1 is a var-var, var_ptr is a symbol table slot which may be + * invalidated by the binary operation. Turn it into a ref so we + * control the lifetime of the zval slot. */ + ZVAL_MAKE_REF(var_ptr); + goto assign_op_ref; } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); - } while (0); + } if (UNEXPECTED(RETURN_VALUE_USED(opline))) { ZVAL_COPY(EX_VAR(opline->result.var), var_ptr); } +assign_op_end: +