]> granicus.if.org Git - php/commitdiff
Fix #79979: passing value to by-ref param via CUFA crashes
authorChristoph M. Becker <cmbecker69@gmx.de>
Mon, 24 Aug 2020 09:47:31 +0000 (11:47 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 24 Aug 2020 12:08:32 +0000 (14:08 +0200)
If a by-val send is not allowed, we must not do so.  Instead we wrap
the value in a temporary reference.

Closes GH-6000

NEWS
Zend/tests/bug79979.phpt [new file with mode: 0644]
Zend/zend_execute_API.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

diff --git a/NEWS b/NEWS
index 4342041a8dac47ab16af1df2599f1dabd9120dc8..58b0359c2844b06f17d1026724c7f16002054fa3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,10 @@ PHP                                                                        NEWS
 |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
 ?? ??? 2020, PHP 7.4.11
 
+- Core:
+  . Fixed bug #79979 (passing value to by-ref param via CUFA crashes). (cmb,
+    Nikita)
+
 - Calendar:
   . Fixed bug #80007 (Potential type confusion in unixtojd() parameter parsing).
     (Andy Postnikov)
diff --git a/Zend/tests/bug79979.phpt b/Zend/tests/bug79979.phpt
new file mode 100644 (file)
index 0000000..8d7f30b
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+Bug #79979 (passing value to by-ref param via CUF(A) crashes)
+--FILE--
+<?php
+call_user_func_array("str_replace", ["a", "b", "c", 0]);
+
+$cufa = "call_user_func_array";
+$cufa("str_replace", ["a", "b", "c", 0]);
+
+call_user_func_array($cufa, ["str_replace", ["a", "b", "c", 0]]);
+?>
+--EXPECTF--
+Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
+
+Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
index f2881f70543c97e815ef113f3cbc4c24be703dc3..78a041d6ce2a7ce829ef16880e12a15b8d9ead48 100644 (file)
@@ -757,6 +757,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
        for (i=0; i<fci->param_count; i++) {
                zval *param;
                zval *arg = &fci->params[i];
+               zend_bool must_wrap = 0;
 
                if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
                        if (UNEXPECTED(!Z_ISREF_P(arg))) {
@@ -765,12 +766,13 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
                                        ZVAL_NEW_REF(arg, arg);
                                } else if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
                                        /* By-value send is not allowed -- emit a warning,
-                                        * but still perform the call with a by-value send. */
+                                        * and perform the call with the value wrapped in a reference. */
                                        zend_error(E_WARNING,
                                                "Parameter %d to %s%s%s() expected to be a reference, value given", i+1,
                                                func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
                                                func->common.scope ? "::" : "",
                                                ZSTR_VAL(func->common.function_name));
+                                       must_wrap = 1;
                                        if (UNEXPECTED(EG(exception))) {
                                                ZEND_CALL_NUM_ARGS(call) = i;
                                                zend_vm_stack_free_args(call);
@@ -791,7 +793,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
                }
 
                param = ZEND_CALL_ARG(call, i+1);
-               ZVAL_COPY(param, arg);
+               if (EXPECTED(!must_wrap)) {
+                       ZVAL_COPY(param, arg);
+               } else {
+                       ZVAL_NEW_REF(param, arg);
+               }
        }
 
        if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
index a5a2070b437b230581fac677523ff5a02c004e08..fa70bf9f966adce9edf5679aa93b6329d5e0cae0 100644 (file)
@@ -5128,6 +5128,7 @@ ZEND_VM_C_LABEL(send_array):
                                arg_num = 1;
                                param = ZEND_CALL_ARG(EX(call), 1);
                                ZEND_HASH_FOREACH_VAL(ht, arg) {
+                                       zend_bool must_wrap = 0;
                                        if (skip > 0) {
                                                skip--;
                                                continue;
@@ -5139,6 +5140,7 @@ ZEND_VM_C_LABEL(send_array):
                                                                /* By-value send is not allowed -- emit a warning,
                                                                 * but still perform the call. */
                                                                zend_param_must_be_ref(EX(call)->func, arg_num);
+                                                               must_wrap = 1;
                                                        }
                                                }
                                        } else {
@@ -5148,7 +5150,11 @@ ZEND_VM_C_LABEL(send_array):
                                                        arg = Z_REFVAL_P(arg);
                                                }
                                        }
-                                       ZVAL_COPY(param, arg);
+                                       if (EXPECTED(!must_wrap)) {
+                                               ZVAL_COPY(param, arg);
+                                       } else {
+                                               ZVAL_NEW_REF(param, arg);
+                                       }
                                        ZEND_CALL_NUM_ARGS(EX(call))++;
                                        arg_num++;
                                        param++;
@@ -5160,12 +5166,14 @@ ZEND_VM_C_LABEL(send_array):
                        arg_num = 1;
                        param = ZEND_CALL_ARG(EX(call), 1);
                        ZEND_HASH_FOREACH_VAL(ht, arg) {
+                               zend_bool must_wrap = 0;
                                if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                        if (UNEXPECTED(!Z_ISREF_P(arg))) {
                                                if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                                        /* By-value send is not allowed -- emit a warning,
                                                         * but still perform the call. */
                                                        zend_param_must_be_ref(EX(call)->func, arg_num);
+                                                       must_wrap = 1;
                                                }
                                        }
                                } else {
@@ -5175,7 +5183,11 @@ ZEND_VM_C_LABEL(send_array):
                                                arg = Z_REFVAL_P(arg);
                                        }
                                }
-                               ZVAL_COPY(param, arg);
+                               if (EXPECTED(!must_wrap)) {
+                                       ZVAL_COPY(param, arg);
+                               } else {
+                                       ZVAL_NEW_REF(param, arg);
+                               }
                                ZEND_CALL_NUM_ARGS(EX(call))++;
                                arg_num++;
                                param++;
index 6aa34bbd1094f83f295f0f40a23e6d7e2dc5ff11..02b0ff24d15b65ea658df384c0b5481700904e8b 100644 (file)
@@ -2072,6 +2072,7 @@ send_array:
                                arg_num = 1;
                                param = ZEND_CALL_ARG(EX(call), 1);
                                ZEND_HASH_FOREACH_VAL(ht, arg) {
+                                       zend_bool must_wrap = 0;
                                        if (skip > 0) {
                                                skip--;
                                                continue;
@@ -2083,6 +2084,7 @@ send_array:
                                                                /* By-value send is not allowed -- emit a warning,
                                                                 * but still perform the call. */
                                                                zend_param_must_be_ref(EX(call)->func, arg_num);
+                                                               must_wrap = 1;
                                                        }
                                                }
                                        } else {
@@ -2092,7 +2094,11 @@ send_array:
                                                        arg = Z_REFVAL_P(arg);
                                                }
                                        }
-                                       ZVAL_COPY(param, arg);
+                                       if (EXPECTED(!must_wrap)) {
+                                               ZVAL_COPY(param, arg);
+                                       } else {
+                                               ZVAL_NEW_REF(param, arg);
+                                       }
                                        ZEND_CALL_NUM_ARGS(EX(call))++;
                                        arg_num++;
                                        param++;
@@ -2104,12 +2110,14 @@ send_array:
                        arg_num = 1;
                        param = ZEND_CALL_ARG(EX(call), 1);
                        ZEND_HASH_FOREACH_VAL(ht, arg) {
+                               zend_bool must_wrap = 0;
                                if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                        if (UNEXPECTED(!Z_ISREF_P(arg))) {
                                                if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
                                                        /* By-value send is not allowed -- emit a warning,
                                                         * but still perform the call. */
                                                        zend_param_must_be_ref(EX(call)->func, arg_num);
+                                                       must_wrap = 1;
                                                }
                                        }
                                } else {
@@ -2119,7 +2127,11 @@ send_array:
                                                arg = Z_REFVAL_P(arg);
                                        }
                                }
-                               ZVAL_COPY(param, arg);
+                               if (EXPECTED(!must_wrap)) {
+                                       ZVAL_COPY(param, arg);
+                               } else {
+                                       ZVAL_NEW_REF(param, arg);
+                               }
                                ZEND_CALL_NUM_ARGS(EX(call))++;
                                arg_num++;
                                param++;