diff --git a/Zend/tests/gh10085_1.phpt b/Zend/tests/gh10085_1.phpt new file mode 100644 index 0000000000000..cc11c96a09d32 --- /dev/null +++ b/Zend/tests/gh10085_1.phpt @@ -0,0 +1,22 @@ +--TEST-- +GH-10085: Assertion in add_function_array() +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + array(2) { + [0]=> + array(0) { + } + [1]=> + int(0) + } + [1]=> + int(0) +} diff --git a/Zend/tests/gh10085_2.phpt b/Zend/tests/gh10085_2.phpt new file mode 100644 index 0000000000000..7895999f2cd05 --- /dev/null +++ b/Zend/tests/gh10085_2.phpt @@ -0,0 +1,25 @@ +--TEST-- +GH-10085: Assertion in add_function_array() +--FILE-- + +--EXPECT-- +array(2) { + [0]=> + array(2) { + [0]=> + array(0) { + } + [1]=> + int(0) + } + [1]=> + int(0) +} diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index d16699d698fea..dcb70a2d448b0 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1045,10 +1045,14 @@ static zend_never_inline void ZEND_FASTCALL add_function_array(zval *result, zva } if (result != op1) { ZVAL_ARR(result, zend_array_dup(Z_ARR_P(op1))); + zend_hash_merge(Z_ARRVAL_P(result), Z_ARRVAL_P(op2), zval_add_ref, 0); } else { - SEPARATE_ARRAY(result); + zval tmp; + ZVAL_COPY_VALUE(&tmp, result); + SEPARATE_ARRAY(&tmp); + zend_hash_merge(Z_ARRVAL_P(&tmp), Z_ARRVAL_P(op2), zval_add_ref, 0); + ZVAL_COPY_VALUE(result, &tmp); } - zend_hash_merge(Z_ARRVAL_P(result), Z_ARRVAL_P(op2), zval_add_ref, 0); } /* }}} */ diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 0b6604217fa35..c544fbaa4a488 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -1154,12 +1154,23 @@ ZEND_VM_HANDLER(27, ZEND_ASSIGN_DIM_OP, VAR|CV, CONST|TMPVAR|UNUSED|NEXT|CV, OP) zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = GET_OP1_OBJ_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { ZEND_VM_C_LABEL(assign_dim_op_array): + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); ZEND_VM_C_LABEL(assign_dim_op_new_array): @@ -1181,8 +1192,6 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (OP2_TYPE != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -1193,6 +1202,9 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -1232,6 +1244,8 @@ ZEND_VM_C_LABEL(assign_dim_op_new_array): ZEND_VM_C_GOTO(assign_dim_op_ret_null); } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; ZEND_VM_C_GOTO(assign_dim_op_new_array); } else { dim = GET_OP2_ZVAL_PTR(BP_VAR_R); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 21b927c02b895..473749f31aa98 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -22872,12 +22872,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -22899,8 +22910,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -22911,6 +22920,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -22950,6 +22962,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CONST_H goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = RT_CONSTANT(opline, opline->op2); @@ -25771,12 +25785,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -25798,8 +25823,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -25810,6 +25833,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -25849,6 +25875,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_TMPVAR_ goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); @@ -28172,12 +28200,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -28199,8 +28238,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -28211,6 +28248,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -28250,6 +28290,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_UNUSED_ goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = NULL; @@ -30068,12 +30110,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = _get_zval_ptr_ptr_var(opline->op1.var EXECUTE_DATA_CC); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -30095,8 +30148,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -30107,6 +30158,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -30146,6 +30200,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_VAR_CV_HAND goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC); @@ -41145,12 +41201,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -41172,8 +41239,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_CONST != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -41184,6 +41249,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -41223,6 +41291,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CONST_HA goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = RT_CONSTANT(opline, opline->op2); @@ -44985,12 +45055,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -45012,8 +45093,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if ((IS_TMP_VAR|IS_VAR) != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -45024,6 +45103,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -45063,6 +45145,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_TMPVAR_H goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_var(opline->op2.var EXECUTE_DATA_CC); @@ -47975,12 +48059,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -48002,8 +48097,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_UNUSED != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -48014,6 +48107,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -48053,6 +48149,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_UNUSED_H goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = NULL; @@ -50402,12 +50500,23 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL zval *var_ptr; zval *value, *container, *dim; HashTable *ht; + zend_array *value_array; SAVE_OPLINE(); container = EX_VAR(opline->op1.var); if (EXPECTED(Z_TYPE_P(container) == IS_ARRAY)) { assign_dim_op_array: + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + if (UNEXPECTED(Z_ISREF_P(value) && Z_TYPE_P(Z_REFVAL_P(value)) == IS_ARRAY && Z_ARRVAL_P(Z_REFVAL_P(value)) == Z_ARRVAL_P(container) && !(GC_FLAGS(Z_ARRVAL_P(container)) & GC_IMMUTABLE))) { + /* The binary OP would normally deref the reference, so an increase in RC would only be done later. + * We need to do this here already to do a correct array separation in case the value is related to the array. + * The only case where this would be problematic is when the container and value are the same array. */ + value_array = Z_ARR_P(Z_REFVAL_P(value)); + GC_ADDREF(value_array); + } else { + value_array = NULL; + } SEPARATE_ARRAY(container); ht = Z_ARRVAL_P(container); assign_dim_op_new_array: @@ -50429,8 +50538,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL } } - value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); - do { if (IS_CV != IS_UNUSED && UNEXPECTED(Z_ISREF_P(var_ptr))) { zend_reference *ref = Z_REF_P(var_ptr); @@ -50441,6 +50548,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL } } zend_binary_op(var_ptr, var_ptr, value OPLINE_CC); + if (UNEXPECTED(value_array)) { + GC_DTOR_NO_REF(value_array); + } } while (0); if (UNEXPECTED(RETURN_VALUE_USED(opline))) { @@ -50480,6 +50590,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_ASSIGN_DIM_OP_SPEC_CV_CV_HANDL goto assign_dim_op_ret_null; } } + value = get_op_data_zval_ptr_r((opline+1)->op1_type, (opline+1)->op1); + value_array = NULL; goto assign_dim_op_new_array; } else { dim = _get_zval_ptr_cv_BP_VAR_R(opline->op2.var EXECUTE_DATA_CC);