]> granicus.if.org Git - php/commitdiff
Use RW fetch for argument unpacking
authorNikita Popov <nikita.ppv@gmail.com>
Tue, 30 Jul 2019 08:05:12 +0000 (10:05 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Tue, 30 Jul 2019 08:07:08 +0000 (10:07 +0200)
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.

Zend/tests/arg_unpack/nested_by_ref.phpt [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/Zend/tests/arg_unpack/nested_by_ref.phpt b/Zend/tests/arg_unpack/nested_by_ref.phpt
new file mode 100644 (file)
index 0000000..599c181
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+By-ref unpacking of a nested access
+--FILE--
+<?php
+
+function inc(&$var) {
+    $var++;
+}
+
+$ary = [[1]];
+inc(...$ary[0]);
+var_dump($ary);
+
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  array(1) {
+    [0]=>
+    int(2)
+  }
+}
index 10a6b294f7acc3bb86095cc43279d1b170356356..dfa845cd73558c4c868d17287f79db48c668d51d 100644 (file)
@@ -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);
index 930e1cc746e257779651798da3db300ec0a66c32..80c8ff68bcfb0ad689287d1cba5752c5812335a2 100644 (file)
@@ -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):
index 6dfa6f2899a549d058245943e136383c68a15fe1..23d41dbdbd3bf19fa81a79e9f413dee9bed76b97 100644 (file)
@@ -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: