From: Nikita Popov Date: Wed, 2 Sep 2020 08:23:44 +0000 (+0200) Subject: Fix by-ref list assign LIST_W+MAKE_REF separation X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8b6b2bda093b26640d0d1d640e4566684df88011;p=php Fix by-ref list assign LIST_W+MAKE_REF separation Shift the responsibility for emitting MAKE_REF to the list assignment code, to make sure that LIST_W and MAKE_REF are directly adjacent, and there are no opcodes in between that could modify the LIST_W result. Additionally, adjust the zend_wrong_string_offset() code to not perform a loop over opcodes and assert that the next opcode is a relevant one. The VM write-safety model requires this. This is a followup to a07c1f56aac1c0f6c8334760009b678cbf9d6138 and the full fix for oss-fuzz #25352. --- diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 190e7e49d8..8d76148fc2 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2984,10 +2984,10 @@ static void zend_compile_list_assign( zend_handle_numeric_dim(opline, &dim_node); } + if (elem_ast->attr) { + zend_emit_op(&fetch_result, ZEND_MAKE_REF, &fetch_result, NULL); + } if (var_ast->kind == ZEND_AST_ARRAY) { - if (elem_ast->attr) { - zend_emit_op(&fetch_result, ZEND_MAKE_REF, &fetch_result, NULL); - } zend_compile_list_assign(NULL, var_ast, &fetch_result, var_ast->attr); } else if (elem_ast->attr) { zend_emit_assign_ref_znode(var_ast, &fetch_result); @@ -3180,6 +3180,7 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */ if ((target_ast->kind != ZEND_AST_VAR || target_ast->child[0]->kind != ZEND_AST_ZVAL) + && source_ast->kind != ZEND_AST_ZNODE && source_node.op_type != IS_CV) { /* Both LHS and RHS expressions may modify the same data structure, * and the modification during RHS evaluation may dangle the pointer diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 52260e9f81..6e66ba72d6 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1374,7 +1374,6 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) { const char *msg = NULL; const zend_op *opline = EX(opline); - const zend_op *end; uint32_t var; if (UNEXPECTED(EG(exception) != NULL)) { @@ -1396,78 +1395,75 @@ static zend_never_inline ZEND_COLD void zend_wrong_string_offset(EXECUTE_DATA_D) /* TODO: Encode the "reason" into opline->extended_value??? */ var = opline->result.var; opline++; - end = EG(current_execute_data)->func->op_array.opcodes + - EG(current_execute_data)->func->op_array.last; - while (opline < end) { - if (opline->op1_type == IS_VAR && opline->op1.var == var) { - switch (opline->opcode) { - case ZEND_FETCH_OBJ_W: - case ZEND_FETCH_OBJ_RW: - case ZEND_FETCH_OBJ_FUNC_ARG: - case ZEND_FETCH_OBJ_UNSET: - case ZEND_ASSIGN_OBJ: - case ZEND_ASSIGN_OBJ_OP: - case ZEND_ASSIGN_OBJ_REF: - msg = "Cannot use string offset as an object"; - break; - case ZEND_FETCH_DIM_W: - case ZEND_FETCH_DIM_RW: - case ZEND_FETCH_DIM_FUNC_ARG: - case ZEND_FETCH_DIM_UNSET: - case ZEND_FETCH_LIST_W: - case ZEND_ASSIGN_DIM: - case ZEND_ASSIGN_DIM_OP: - msg = "Cannot use string offset as an array"; - break; - case ZEND_ASSIGN_STATIC_PROP_OP: - case ZEND_ASSIGN_OP: - msg = "Cannot use assign-op operators with string offsets"; - break; - case ZEND_PRE_INC_OBJ: - case ZEND_PRE_DEC_OBJ: - case ZEND_POST_INC_OBJ: - case ZEND_POST_DEC_OBJ: - case ZEND_PRE_INC: - case ZEND_PRE_DEC: - case ZEND_POST_INC: - case ZEND_POST_DEC: - msg = "Cannot increment/decrement string offsets"; - break; - case ZEND_ASSIGN_REF: - case ZEND_ADD_ARRAY_ELEMENT: - case ZEND_INIT_ARRAY: - case ZEND_MAKE_REF: - msg = "Cannot create references to/from string offsets"; - break; - case ZEND_RETURN_BY_REF: - case ZEND_VERIFY_RETURN_TYPE: - msg = "Cannot return string offsets by reference"; - break; - case ZEND_UNSET_DIM: - case ZEND_UNSET_OBJ: - msg = "Cannot unset string offsets"; - break; - case ZEND_YIELD: - msg = "Cannot yield string offsets by reference"; - break; - case ZEND_SEND_REF: - case ZEND_SEND_VAR_EX: - case ZEND_SEND_FUNC_ARG: - msg = "Only variables can be passed by reference"; - break; - case ZEND_FE_RESET_RW: - msg = "Cannot iterate on string offsets by reference"; - break; - EMPTY_SWITCH_DEFAULT_CASE(); - } - break; - } - if (opline->op2_type == IS_VAR && opline->op2.var == var) { - ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF); - msg = "Cannot create references to/from string offsets"; - break; + ZEND_ASSERT(opline < execute_data->func->op_array.opcodes + + execute_data->func->op_array.last); + if (opline->op1_type == IS_VAR && opline->op1.var == var) { + switch (opline->opcode) { + case ZEND_FETCH_OBJ_W: + case ZEND_FETCH_OBJ_RW: + case ZEND_FETCH_OBJ_FUNC_ARG: + case ZEND_FETCH_OBJ_UNSET: + case ZEND_ASSIGN_OBJ: + case ZEND_ASSIGN_OBJ_OP: + case ZEND_ASSIGN_OBJ_REF: + msg = "Cannot use string offset as an object"; + break; + case ZEND_FETCH_DIM_W: + case ZEND_FETCH_DIM_RW: + case ZEND_FETCH_DIM_FUNC_ARG: + case ZEND_FETCH_DIM_UNSET: + case ZEND_FETCH_LIST_W: + case ZEND_ASSIGN_DIM: + case ZEND_ASSIGN_DIM_OP: + msg = "Cannot use string offset as an array"; + break; + case ZEND_ASSIGN_STATIC_PROP_OP: + case ZEND_ASSIGN_OP: + msg = "Cannot use assign-op operators with string offsets"; + break; + case ZEND_PRE_INC_OBJ: + case ZEND_PRE_DEC_OBJ: + case ZEND_POST_INC_OBJ: + case ZEND_POST_DEC_OBJ: + case ZEND_PRE_INC: + case ZEND_PRE_DEC: + case ZEND_POST_INC: + case ZEND_POST_DEC: + msg = "Cannot increment/decrement string offsets"; + break; + case ZEND_ASSIGN_REF: + case ZEND_ADD_ARRAY_ELEMENT: + case ZEND_INIT_ARRAY: + case ZEND_MAKE_REF: + msg = "Cannot create references to/from string offsets"; + break; + case ZEND_RETURN_BY_REF: + case ZEND_VERIFY_RETURN_TYPE: + msg = "Cannot return string offsets by reference"; + break; + case ZEND_UNSET_DIM: + case ZEND_UNSET_OBJ: + msg = "Cannot unset string offsets"; + break; + case ZEND_YIELD: + msg = "Cannot yield string offsets by reference"; + break; + case ZEND_SEND_REF: + case ZEND_SEND_VAR_EX: + case ZEND_SEND_FUNC_ARG: + msg = "Only variables can be passed by reference"; + break; + case ZEND_FE_RESET_RW: + msg = "Cannot iterate on string offsets by reference"; + break; + EMPTY_SWITCH_DEFAULT_CASE(); } - opline++; + break; + } + if (opline->op2_type == IS_VAR && opline->op2.var == var) { + ZEND_ASSERT(opline->opcode == ZEND_ASSIGN_REF); + msg = "Cannot create references to/from string offsets"; + break; } break; EMPTY_SWITCH_DEFAULT_CASE();