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: +