]> granicus.if.org Git - php/commitdiff
Merge branch 'PHP-7.4' into master
authorChristoph M. Becker <cmbecker69@gmx.de>
Mon, 24 Aug 2020 13:02:15 +0000 (15:02 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Mon, 24 Aug 2020 13:03:26 +0000 (15:03 +0200)
* PHP-7.4:
  Fix #79979: passing value to by-ref param via CUFA crashes

1  2 
Zend/tests/bug79979.phpt
Zend/zend_execute_API.c
Zend/zend_vm_def.h
Zend/zend_vm_execute.h

index 0000000000000000000000000000000000000000,8d7f30b7c83f072a5e4a5b3946aec1068c0a3bb3..c41d3889e965e5767af4e94b6544a1d0f8b2e7c1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,17 +1,17 @@@
 -Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
+ --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: str_replace(): Argument #4 ($replace_count) must be passed by 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: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d
++Warning: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d
index b3d6319560dc25440dcb6b2cd9696bbf7598b7c8,78a041d6ce2a7ce829ef16880e12a15b8d9ead48..ece36798226b83ebbd5de4fbffa5bdc09ce26837
@@@ -732,24 -755,26 +732,26 @@@ int zend_call_function(zend_fcall_info 
        }
  
        for (i=0; i<fci->param_count; i++) {
 -              zval *param;
 +              zval *param = ZEND_CALL_ARG(call, i+1);
                zval *arg = &fci->params[i];
+               zend_bool must_wrap = 0;
 +              if (UNEXPECTED(Z_ISUNDEF_P(arg))) {
 +                      /* Allow forwarding undef slots. This is only used by Closure::__invoke(). */
 +                      ZVAL_UNDEF(param);
 +                      ZEND_ADD_CALL_FLAG(call, ZEND_CALL_MAY_HAVE_UNDEF);
 +                      continue;
 +              }
  
                if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
                        if (UNEXPECTED(!Z_ISREF_P(arg))) {
 -                              if (!fci->no_separation) {
 -                                      /* Separation is enabled -- create a ref */
 -                                      ZVAL_NEW_REF(arg, arg);
 -                              } else if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
 +                              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));
 +                                      zend_param_must_be_ref(func, i + 1);
+                                       must_wrap = 1;
                                        if (UNEXPECTED(EG(exception))) {
                                                ZEND_CALL_NUM_ARGS(call) = i;
 +cleanup_args:
                                                zend_vm_stack_free_args(call);
                                                zend_vm_stack_free_call_frame(call);
                                                if (EG(current_execute_data) == &dummy_execute_data) {
                        }
                }
  
-               ZVAL_COPY(param, arg);
 -              param = ZEND_CALL_ARG(call, i+1);
+               if (EXPECTED(!must_wrap)) {
+                       ZVAL_COPY(param, arg);
+               } else {
+                       ZVAL_NEW_REF(param, arg);
+               }
        }
  
-                                                * but still perform the call with a by-value send. */
 +      if (fci->named_params) {
 +              zend_string *name;
 +              zval *arg;
 +              uint32_t arg_num = ZEND_CALL_NUM_ARGS(call) + 1;
 +              zend_bool have_named_params = 0;
++              zend_bool must_wrap = 0;
 +              ZEND_HASH_FOREACH_STR_KEY_VAL(fci->named_params, name, arg) {
 +                      zval *target;
 +                      if (name) {
 +                              void *cache_slot[2] = {NULL, NULL};
 +                              have_named_params = 1;
 +                              target = zend_handle_named_arg(&call, name, &arg_num, cache_slot);
 +                              if (!target) {
 +                                      goto cleanup_args;
 +                              }
 +                      } else {
 +                              if (have_named_params) {
 +                                      zend_throw_error(NULL,
 +                                              "Cannot use positional argument after named argument");
 +                                      goto cleanup_args;
 +                              }
 +
 +                              zend_vm_stack_extend_call_frame(&call, arg_num - 1, 1);
 +                              target = ZEND_CALL_ARG(call, arg_num);
 +                      }
 +
 +                      if (ARG_SHOULD_BE_SENT_BY_REF(func, arg_num)) {
 +                              if (UNEXPECTED(!Z_ISREF_P(arg))) {
 +                                      if (!ARG_MAY_BE_SENT_BY_REF(func, arg_num)) {
 +                                              /* By-value send is not allowed -- emit a warning,
-                       ZVAL_COPY(target, arg);
++                                               * and perform the call with the value wrapped in a reference. */
 +                                              zend_param_must_be_ref(func, arg_num);
++                                              must_wrap = 1;
 +                                              if (UNEXPECTED(EG(exception))) {
 +                                                      goto cleanup_args;
 +                                              }
 +                                      }
 +                              }
 +                      } else {
 +                              if (Z_ISREF_P(arg) &&
 +                                      !(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) {
 +                                      /* don't separate references for __call */
 +                                      arg = Z_REFVAL_P(arg);
 +                              }
 +                      }
 +
++                      if (EXPECTED(!must_wrap)) {
++                              ZVAL_COPY(target, arg);
++                      } else {
++                              ZVAL_NEW_REF(target, arg);
++                      }
 +                      if (!name) {
 +                              ZEND_CALL_NUM_ARGS(call)++;
 +                              arg_num++;
 +                      }
 +              } ZEND_HASH_FOREACH_END();
 +      }
 +
        if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {
                uint32_t call_info;
  
index b92928e3b0dd06a773d015e868f68b5a594a152d,fa70bf9f966adce9edf5679aa93b6329d5e0cae0..805f8f0a3e0f0abb59b328f2ddf0654565428f36
@@@ -5236,23 -5165,8 +5242,24 @@@ ZEND_VM_C_LABEL(send_array)
                        zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
                        arg_num = 1;
                        param = ZEND_CALL_ARG(EX(call), 1);
 -                      ZEND_HASH_FOREACH_VAL(ht, arg) {
 +                      have_named_params = 0;
 +                      ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
 +                              if (name) {
 +                                      void *cache_slot[2] = {NULL, NULL};
 +                                      have_named_params = 1;
 +                                      param = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
 +                                      if (!param) {
 +                                              FREE_OP1();
 +                                              HANDLE_EXCEPTION();
 +                                      }
 +                              } else if (have_named_params) {
 +                                      zend_throw_error(NULL,
 +                                              "Cannot use positional argument after named argument");
 +                                      FREE_OP1();
 +                                      HANDLE_EXCEPTION();
 +                              }
 +
+                               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)) {
                                                arg = Z_REFVAL_P(arg);
                                        }
                                }
-                               ZVAL_COPY(param, arg);
 +
 -                              ZEND_CALL_NUM_ARGS(EX(call))++;
 -                              arg_num++;
 -                              param++;
+                               if (EXPECTED(!must_wrap)) {
+                                       ZVAL_COPY(param, arg);
+                               } else {
+                                       ZVAL_NEW_REF(param, arg);
+                               }
 +                              if (!name) {
 +                                      ZEND_CALL_NUM_ARGS(EX(call))++;
 +                                      arg_num++;
 +                                      param++;
 +                              }
                        } ZEND_HASH_FOREACH_END();
                }
        }
index 7a50d99d299e85393ee7021c75bf8060e7dc88f6,02b0ff24d15b65ea658df384c0b5481700904e8b..d87edfc570e56ffbd5589893bb20962c469467e3
@@@ -2131,23 -2109,8 +2137,24 @@@ send_array
                        zend_vm_stack_extend_call_frame(&EX(call), 0, zend_hash_num_elements(ht));
                        arg_num = 1;
                        param = ZEND_CALL_ARG(EX(call), 1);
 -                      ZEND_HASH_FOREACH_VAL(ht, arg) {
 +                      have_named_params = 0;
 +                      ZEND_HASH_FOREACH_STR_KEY_VAL(ht, name, arg) {
 +                              if (name) {
 +                                      void *cache_slot[2] = {NULL, NULL};
 +                                      have_named_params = 1;
 +                                      param = zend_handle_named_arg(&EX(call), name, &arg_num, cache_slot);
 +                                      if (!param) {
 +                                              FREE_OP(opline->op1_type, opline->op1.var);
 +                                              HANDLE_EXCEPTION();
 +                                      }
 +                              } else if (have_named_params) {
 +                                      zend_throw_error(NULL,
 +                                              "Cannot use positional argument after named argument");
 +                                      FREE_OP(opline->op1_type, opline->op1.var);
 +                                      HANDLE_EXCEPTION();
 +                              }
 +
+                               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)) {
                                                arg = Z_REFVAL_P(arg);
                                        }
                                }
-                               ZVAL_COPY(param, arg);
 +
 -                              ZEND_CALL_NUM_ARGS(EX(call))++;
 -                              arg_num++;
 -                              param++;
+                               if (EXPECTED(!must_wrap)) {
+                                       ZVAL_COPY(param, arg);
+                               } else {
+                                       ZVAL_NEW_REF(param, arg);
+                               }
 +                              if (!name) {
 +                                      ZEND_CALL_NUM_ARGS(EX(call))++;
 +                                      arg_num++;
 +                                      param++;
 +                              }
                        } ZEND_HASH_FOREACH_END();
                }
        }