]> granicus.if.org Git - php/commitdiff
Fix argument unpacking across stack pages
authorNikita Popov <nikic@php.net>
Sat, 18 Jan 2014 11:30:44 +0000 (12:30 +0100)
committerNikita Popov <nikic@php.net>
Sat, 18 Jan 2014 13:41:33 +0000 (14:41 +0100)
If multiple unpacks were used (or mixed with normal arguments)
parts of the arguments could land on different stack pages. If
this occurs the arguments will now be copied to a new stack page.

The code used to do this is copied verbatim from the PHP 5.4 branch
and only modified to reduce the amount of inlined code.

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

diff --git a/Zend/tests/arg_unpack/many_args.phpt b/Zend/tests/arg_unpack/many_args.phpt
new file mode 100644 (file)
index 0000000..0ef5a30
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+Argument unpacking with many arguments
+--FILE--
+<?php
+
+function fn(...$args) {
+    var_dump(count($args));
+}
+
+$array = array_fill(0, 10000, 42);
+fn(...$array, ...$array);
+
+?>
+--EXPECT--
+int(20000)
index 174b165c7e73204273d16943a8a2058518e292e5..31caceecbe90224cb7b3cf4cf7db75e36ebdbf40 100644 (file)
@@ -1691,6 +1691,42 @@ static zend_always_inline zend_bool zend_is_by_ref_func_arg_fetch(zend_op *oplin
 }
 /* }}} */
 
+static void **zend_vm_stack_push_args_with_copy(int count TSRMLS_DC) /* {{{ */
+{
+       zend_vm_stack p = EG(argument_stack);
+
+       zend_vm_stack_extend(count + 1 TSRMLS_CC);
+
+       EG(argument_stack)->top += count;
+       *(EG(argument_stack)->top) = (void*)(zend_uintptr_t)count;
+       while (count-- > 0) {
+               void *data = *(--p->top);
+
+               if (UNEXPECTED(p->top == ZEND_VM_STACK_ELEMETS(p))) {
+                       zend_vm_stack r = p;
+
+                       EG(argument_stack)->prev = p->prev;
+                       p = p->prev;
+                       efree(r);
+               }
+               *(ZEND_VM_STACK_ELEMETS(EG(argument_stack)) + count) = data;
+       }
+       return EG(argument_stack)->top++;
+}
+/* }}} */
+
+static zend_always_inline void** zend_vm_stack_push_args(int count TSRMLS_DC) /* {{{ */
+{
+       if (UNEXPECTED(EG(argument_stack)->top - ZEND_VM_STACK_ELEMETS(EG(argument_stack)) < count)
+               || UNEXPECTED(EG(argument_stack)->top == EG(argument_stack)->end)) {
+               return zend_vm_stack_push_args_with_copy(count TSRMLS_CC);
+       }
+       *(EG(argument_stack)->top) = (void*)(zend_uintptr_t)count;
+       return EG(argument_stack)->top++;
+}
+/* }}} */
+
+
 #define ZEND_VM_NEXT_OPCODE() \
        CHECK_SYMBOL_TABLES() \
        ZEND_VM_INC_OPCODE(); \
index 37af82cef2096b3f09fdff6858b18b9d577e1885..1e8a8315515e8ff905b1598ca92a00a91620b2f9 100644 (file)
@@ -1947,8 +1947,12 @@ ZEND_VM_HELPER(zend_do_fcall_common_helper, ANY, ANY)
        }
 
        num_args = opline->extended_value + EX(call)->num_additional_args;
-       EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
-       zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
+       if (EX(call)->num_additional_args) {
+               EX(function_state).arguments = zend_vm_stack_push_args(num_args TSRMLS_CC);
+       } else {
+               EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
+               zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
+       }
        LOAD_OPLINE();
 
        if (fbc->type == ZEND_INTERNAL_FUNCTION) {
index 0651389f74131b96583f89464a95ca5874f2086b..7e613cd6e84c89e03c711485b6dadafa2bae8ecd 100644 (file)
@@ -527,8 +527,12 @@ static int ZEND_FASTCALL zend_do_fcall_common_helper_SPEC(ZEND_OPCODE_HANDLER_AR
        }
 
        num_args = opline->extended_value + EX(call)->num_additional_args;
-       EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
-       zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
+       if (EX(call)->num_additional_args) {
+               EX(function_state).arguments = zend_vm_stack_push_args(num_args TSRMLS_CC);
+       } else {
+               EX(function_state).arguments = zend_vm_stack_top(TSRMLS_C);
+               zend_vm_stack_push((void*)(zend_uintptr_t) num_args TSRMLS_CC);
+       }
        LOAD_OPLINE();
 
        if (fbc->type == ZEND_INTERNAL_FUNCTION) {