From: Nikita Popov Date: Tue, 30 Jul 2019 08:05:12 +0000 (+0200) Subject: Use RW fetch for argument unpacking X-Git-Tag: php-7.4.0beta2~18 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6913ec3282149914e999d91b056fe1cc68d15ed7;p=php Use RW fetch for argument unpacking Argument unpacking may need to create references inside the array that is being unpacked. However, it currently can only do this if a plain variable is unpacked, not for any nested accesses, because the value is fetched for read. Resolve this by fetching the operands for RW. --- diff --git a/Zend/tests/arg_unpack/nested_by_ref.phpt b/Zend/tests/arg_unpack/nested_by_ref.phpt new file mode 100644 index 0000000000..599c181c2c --- /dev/null +++ b/Zend/tests/arg_unpack/nested_by_ref.phpt @@ -0,0 +1,22 @@ +--TEST-- +By-ref unpacking of a nested access +--FILE-- + +--EXPECT-- +array(1) { + [0]=> + array(1) { + [0]=> + int(2) + } +} diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 10a6b294f7..dfa845cd73 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -2963,7 +2963,15 @@ uint32_t zend_compile_args(zend_ast *ast, zend_function *fbc) /* {{{ */ uses_arg_unpack = 1; fbc = NULL; - zend_compile_expr(&arg_node, arg->child[0]); + /* Unpacking may need to create interior references in the unpacked array, + * but apart from that does not have any other reference semantics: It should + * generate a notice if the variable does not exist and it should not convert + * the variable itself into a reference. As such, use an RW fetch. */ + if (zend_is_variable(arg->child[0])) { + zend_compile_var(&arg_node, arg->child[0], BP_VAR_RW, 0); + } else { + zend_compile_expr(&arg_node, arg->child[0]); + } opline = zend_emit_op(NULL, ZEND_SEND_UNPACK, &arg_node, NULL); opline->op2.num = arg_count; opline->result.var = (uint32_t)(zend_intptr_t)ZEND_CALL_ARG(NULL, arg_count); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 930e1cc746..80c8ff68bc 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4920,7 +4920,11 @@ ZEND_VM_HANDLER(165, ZEND_SEND_UNPACK, ANY, ANY) int arg_num; SAVE_OPLINE(); - args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + if (OP1_TYPE & (IS_VAR|IS_CV)) { + args = GET_OP1_ZVAL_PTR_PTR_UNDEF(BP_VAR_RW); + } else { + args = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); + } arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1; ZEND_VM_C_LABEL(send_again): diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 6dfa6f2899..23d41dbdbd 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1862,7 +1862,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_UNPACK_SPEC_HANDLER(ZEND_ int arg_num; SAVE_OPLINE(); - args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R); + if (opline->op1_type & (IS_VAR|IS_CV)) { + args = get_zval_ptr_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_RW); + } else { + args = get_zval_ptr_undef(opline->op1_type, opline->op1, &free_op1, BP_VAR_R); + } arg_num = ZEND_CALL_NUM_ARGS(EX(call)) + 1; send_again: