summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Zend/tests/bug79979.phpt17
-rw-r--r--Zend/zend_execute_API.c20
-rw-r--r--Zend/zend_vm_def.h16
-rw-r--r--Zend/zend_vm_execute.h16
4 files changed, 61 insertions, 8 deletions
diff --git a/Zend/tests/bug79979.phpt b/Zend/tests/bug79979.phpt
new file mode 100644
index 0000000000..c41d3889e9
--- /dev/null
+++ b/Zend/tests/bug79979.phpt
@@ -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: 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
+
+Warning: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d
diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c
index b3d6319560..ece3679822 100644
--- a/Zend/zend_execute_API.c
+++ b/Zend/zend_execute_API.c
@@ -734,6 +734,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 = 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);
@@ -745,8 +746,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
if (UNEXPECTED(!Z_ISREF_P(arg))) {
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_param_must_be_ref(func, i + 1);
+ must_wrap = 1;
if (UNEXPECTED(EG(exception))) {
ZEND_CALL_NUM_ARGS(call) = i;
cleanup_args:
@@ -767,7 +769,11 @@ cleanup_args:
}
}
- ZVAL_COPY(param, arg);
+ if (EXPECTED(!must_wrap)) {
+ ZVAL_COPY(param, arg);
+ } else {
+ ZVAL_NEW_REF(param, arg);
+ }
}
if (fci->named_params) {
@@ -775,6 +781,7 @@ cleanup_args:
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) {
@@ -799,8 +806,9 @@ cleanup_args:
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,
- * but still perform the call with a by-value send. */
+ * 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;
}
@@ -814,7 +822,11 @@ cleanup_args:
}
}
- ZVAL_COPY(target, arg);
+ if (EXPECTED(!must_wrap)) {
+ ZVAL_COPY(target, arg);
+ } else {
+ ZVAL_NEW_REF(target, arg);
+ }
if (!name) {
ZEND_CALL_NUM_ARGS(call)++;
arg_num++;
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index b92928e3b0..805f8f0a3e 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -5203,6 +5203,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;
@@ -5214,6 +5215,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 {
@@ -5223,7 +5225,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++;
@@ -5253,12 +5259,14 @@ ZEND_VM_C_LABEL(send_array):
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)) {
/* 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 {
@@ -5269,7 +5277,11 @@ ZEND_VM_C_LABEL(send_array):
}
}
- ZVAL_COPY(param, arg);
+ if (EXPECTED(!must_wrap)) {
+ ZVAL_COPY(param, arg);
+ } else {
+ ZVAL_NEW_REF(param, arg);
+ }
if (!name) {
ZEND_CALL_NUM_ARGS(EX(call))++;
arg_num++;
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index 7a50d99d29..d87edfc570 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -2098,6 +2098,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;
@@ -2109,6 +2110,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 {
@@ -2118,7 +2120,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++;
@@ -2148,12 +2154,14 @@ send_array:
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)) {
/* 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 {
@@ -2164,7 +2172,11 @@ send_array:
}
}
- ZVAL_COPY(param, arg);
+ if (EXPECTED(!must_wrap)) {
+ ZVAL_COPY(param, arg);
+ } else {
+ ZVAL_NEW_REF(param, arg);
+ }
if (!name) {
ZEND_CALL_NUM_ARGS(EX(call))++;
arg_num++;