Skip to content

Commit 6b6c2c0

Browse files
committed
Fix #79979: passing value to by-ref param via CUFA crashes
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
1 parent 5ab7b30 commit 6b6c2c0

File tree

5 files changed

+57
-6
lines changed

5 files changed

+57
-6
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? 2020, PHP 7.4.11
44

5+
- Core:
6+
. Fixed bug #79979 (passing value to by-ref param via CUFA crashes). (cmb,
7+
Nikita)
8+
59
- Calendar:
610
. Fixed bug #80007 (Potential type confusion in unixtojd() parameter parsing).
711
(Andy Postnikov)

Zend/tests/bug79979.phpt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Bug #79979 (passing value to by-ref param via CUF(A) crashes)
3+
--FILE--
4+
<?php
5+
call_user_func_array("str_replace", ["a", "b", "c", 0]);
6+
7+
$cufa = "call_user_func_array";
8+
$cufa("str_replace", ["a", "b", "c", 0]);
9+
10+
call_user_func_array($cufa, ["str_replace", ["a", "b", "c", 0]]);
11+
?>
12+
--EXPECTF--
13+
Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
14+
15+
Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d
16+
17+
Warning: Parameter 4 to str_replace() expected to be a reference, value given in %s on line %d

Zend/zend_execute_API.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
757757
for (i=0; i<fci->param_count; i++) {
758758
zval *param;
759759
zval *arg = &fci->params[i];
760+
zend_bool must_wrap = 0;
760761

761762
if (ARG_SHOULD_BE_SENT_BY_REF(func, i + 1)) {
762763
if (UNEXPECTED(!Z_ISREF_P(arg))) {
@@ -765,12 +766,13 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
765766
ZVAL_NEW_REF(arg, arg);
766767
} else if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
767768
/* By-value send is not allowed -- emit a warning,
768-
* but still perform the call with a by-value send. */
769+
* and perform the call with the value wrapped in a reference. */
769770
zend_error(E_WARNING,
770771
"Parameter %d to %s%s%s() expected to be a reference, value given", i+1,
771772
func->common.scope ? ZSTR_VAL(func->common.scope->name) : "",
772773
func->common.scope ? "::" : "",
773774
ZSTR_VAL(func->common.function_name));
775+
must_wrap = 1;
774776
if (UNEXPECTED(EG(exception))) {
775777
ZEND_CALL_NUM_ARGS(call) = i;
776778
zend_vm_stack_free_args(call);
@@ -791,7 +793,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
791793
}
792794

793795
param = ZEND_CALL_ARG(call, i+1);
794-
ZVAL_COPY(param, arg);
796+
if (EXPECTED(!must_wrap)) {
797+
ZVAL_COPY(param, arg);
798+
} else {
799+
ZVAL_NEW_REF(param, arg);
800+
}
795801
}
796802

797803
if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) {

Zend/zend_vm_def.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5128,6 +5128,7 @@ ZEND_VM_C_LABEL(send_array):
51285128
arg_num = 1;
51295129
param = ZEND_CALL_ARG(EX(call), 1);
51305130
ZEND_HASH_FOREACH_VAL(ht, arg) {
5131+
zend_bool must_wrap = 0;
51315132
if (skip > 0) {
51325133
skip--;
51335134
continue;
@@ -5139,6 +5140,7 @@ ZEND_VM_C_LABEL(send_array):
51395140
/* By-value send is not allowed -- emit a warning,
51405141
* but still perform the call. */
51415142
zend_param_must_be_ref(EX(call)->func, arg_num);
5143+
must_wrap = 1;
51425144
}
51435145
}
51445146
} else {
@@ -5148,7 +5150,11 @@ ZEND_VM_C_LABEL(send_array):
51485150
arg = Z_REFVAL_P(arg);
51495151
}
51505152
}
5151-
ZVAL_COPY(param, arg);
5153+
if (EXPECTED(!must_wrap)) {
5154+
ZVAL_COPY(param, arg);
5155+
} else {
5156+
ZVAL_NEW_REF(param, arg);
5157+
}
51525158
ZEND_CALL_NUM_ARGS(EX(call))++;
51535159
arg_num++;
51545160
param++;
@@ -5160,12 +5166,14 @@ ZEND_VM_C_LABEL(send_array):
51605166
arg_num = 1;
51615167
param = ZEND_CALL_ARG(EX(call), 1);
51625168
ZEND_HASH_FOREACH_VAL(ht, arg) {
5169+
zend_bool must_wrap = 0;
51635170
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
51645171
if (UNEXPECTED(!Z_ISREF_P(arg))) {
51655172
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
51665173
/* By-value send is not allowed -- emit a warning,
51675174
* but still perform the call. */
51685175
zend_param_must_be_ref(EX(call)->func, arg_num);
5176+
must_wrap = 1;
51695177
}
51705178
}
51715179
} else {
@@ -5175,7 +5183,11 @@ ZEND_VM_C_LABEL(send_array):
51755183
arg = Z_REFVAL_P(arg);
51765184
}
51775185
}
5178-
ZVAL_COPY(param, arg);
5186+
if (EXPECTED(!must_wrap)) {
5187+
ZVAL_COPY(param, arg);
5188+
} else {
5189+
ZVAL_NEW_REF(param, arg);
5190+
}
51795191
ZEND_CALL_NUM_ARGS(EX(call))++;
51805192
arg_num++;
51815193
param++;

Zend/zend_vm_execute.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
20722072
arg_num = 1;
20732073
param = ZEND_CALL_ARG(EX(call), 1);
20742074
ZEND_HASH_FOREACH_VAL(ht, arg) {
2075+
zend_bool must_wrap = 0;
20752076
if (skip > 0) {
20762077
skip--;
20772078
continue;
@@ -2083,6 +2084,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
20832084
/* By-value send is not allowed -- emit a warning,
20842085
* but still perform the call. */
20852086
zend_param_must_be_ref(EX(call)->func, arg_num);
2087+
must_wrap = 1;
20862088
}
20872089
}
20882090
} else {
@@ -2092,7 +2094,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
20922094
arg = Z_REFVAL_P(arg);
20932095
}
20942096
}
2095-
ZVAL_COPY(param, arg);
2097+
if (EXPECTED(!must_wrap)) {
2098+
ZVAL_COPY(param, arg);
2099+
} else {
2100+
ZVAL_NEW_REF(param, arg);
2101+
}
20962102
ZEND_CALL_NUM_ARGS(EX(call))++;
20972103
arg_num++;
20982104
param++;
@@ -2104,12 +2110,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21042110
arg_num = 1;
21052111
param = ZEND_CALL_ARG(EX(call), 1);
21062112
ZEND_HASH_FOREACH_VAL(ht, arg) {
2113+
zend_bool must_wrap = 0;
21072114
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
21082115
if (UNEXPECTED(!Z_ISREF_P(arg))) {
21092116
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
21102117
/* By-value send is not allowed -- emit a warning,
21112118
* but still perform the call. */
21122119
zend_param_must_be_ref(EX(call)->func, arg_num);
2120+
must_wrap = 1;
21132121
}
21142122
}
21152123
} else {
@@ -2119,7 +2127,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21192127
arg = Z_REFVAL_P(arg);
21202128
}
21212129
}
2122-
ZVAL_COPY(param, arg);
2130+
if (EXPECTED(!must_wrap)) {
2131+
ZVAL_COPY(param, arg);
2132+
} else {
2133+
ZVAL_NEW_REF(param, arg);
2134+
}
21232135
ZEND_CALL_NUM_ARGS(EX(call))++;
21242136
arg_num++;
21252137
param++;

0 commit comments

Comments
 (0)