Skip to content

Commit 5643f34

Browse files
committed
Merge branch 'PHP-7.4' into master
* PHP-7.4: Fix #79979: passing value to by-ref param via CUFA crashes
2 parents bfeb2f6 + 6b6c2c0 commit 5643f34

File tree

4 files changed

+61
-8
lines changed

4 files changed

+61
-8
lines changed

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: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d
14+
15+
Warning: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d
16+
17+
Warning: str_replace(): Argument #4 ($replace_count) must be passed by reference, value given in %s on line %d

Zend/zend_execute_API.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,7 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
734734
for (i=0; i<fci->param_count; i++) {
735735
zval *param = ZEND_CALL_ARG(call, i+1);
736736
zval *arg = &fci->params[i];
737+
zend_bool must_wrap = 0;
737738
if (UNEXPECTED(Z_ISUNDEF_P(arg))) {
738739
/* Allow forwarding undef slots. This is only used by Closure::__invoke(). */
739740
ZVAL_UNDEF(param);
@@ -745,8 +746,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
745746
if (UNEXPECTED(!Z_ISREF_P(arg))) {
746747
if (!ARG_MAY_BE_SENT_BY_REF(func, i + 1)) {
747748
/* By-value send is not allowed -- emit a warning,
748-
* but still perform the call with a by-value send. */
749+
* and perform the call with the value wrapped in a reference. */
749750
zend_param_must_be_ref(func, i + 1);
751+
must_wrap = 1;
750752
if (UNEXPECTED(EG(exception))) {
751753
ZEND_CALL_NUM_ARGS(call) = i;
752754
cleanup_args:
@@ -767,14 +769,19 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
767769
}
768770
}
769771

770-
ZVAL_COPY(param, arg);
772+
if (EXPECTED(!must_wrap)) {
773+
ZVAL_COPY(param, arg);
774+
} else {
775+
ZVAL_NEW_REF(param, arg);
776+
}
771777
}
772778

773779
if (fci->named_params) {
774780
zend_string *name;
775781
zval *arg;
776782
uint32_t arg_num = ZEND_CALL_NUM_ARGS(call) + 1;
777783
zend_bool have_named_params = 0;
784+
zend_bool must_wrap = 0;
778785
ZEND_HASH_FOREACH_STR_KEY_VAL(fci->named_params, name, arg) {
779786
zval *target;
780787
if (name) {
@@ -799,8 +806,9 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
799806
if (UNEXPECTED(!Z_ISREF_P(arg))) {
800807
if (!ARG_MAY_BE_SENT_BY_REF(func, arg_num)) {
801808
/* By-value send is not allowed -- emit a warning,
802-
* but still perform the call with a by-value send. */
809+
* and perform the call with the value wrapped in a reference. */
803810
zend_param_must_be_ref(func, arg_num);
811+
must_wrap = 1;
804812
if (UNEXPECTED(EG(exception))) {
805813
goto cleanup_args;
806814
}
@@ -814,7 +822,11 @@ int zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache) /
814822
}
815823
}
816824

817-
ZVAL_COPY(target, arg);
825+
if (EXPECTED(!must_wrap)) {
826+
ZVAL_COPY(target, arg);
827+
} else {
828+
ZVAL_NEW_REF(target, arg);
829+
}
818830
if (!name) {
819831
ZEND_CALL_NUM_ARGS(call)++;
820832
arg_num++;

Zend/zend_vm_def.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5203,6 +5203,7 @@ ZEND_VM_C_LABEL(send_array):
52035203
arg_num = 1;
52045204
param = ZEND_CALL_ARG(EX(call), 1);
52055205
ZEND_HASH_FOREACH_VAL(ht, arg) {
5206+
zend_bool must_wrap = 0;
52065207
if (skip > 0) {
52075208
skip--;
52085209
continue;
@@ -5214,6 +5215,7 @@ ZEND_VM_C_LABEL(send_array):
52145215
/* By-value send is not allowed -- emit a warning,
52155216
* but still perform the call. */
52165217
zend_param_must_be_ref(EX(call)->func, arg_num);
5218+
must_wrap = 1;
52175219
}
52185220
}
52195221
} else {
@@ -5223,7 +5225,11 @@ ZEND_VM_C_LABEL(send_array):
52235225
arg = Z_REFVAL_P(arg);
52245226
}
52255227
}
5226-
ZVAL_COPY(param, arg);
5228+
if (EXPECTED(!must_wrap)) {
5229+
ZVAL_COPY(param, arg);
5230+
} else {
5231+
ZVAL_NEW_REF(param, arg);
5232+
}
52275233
ZEND_CALL_NUM_ARGS(EX(call))++;
52285234
arg_num++;
52295235
param++;
@@ -5253,12 +5259,14 @@ ZEND_VM_C_LABEL(send_array):
52535259
HANDLE_EXCEPTION();
52545260
}
52555261

5262+
zend_bool must_wrap = 0;
52565263
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
52575264
if (UNEXPECTED(!Z_ISREF_P(arg))) {
52585265
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
52595266
/* By-value send is not allowed -- emit a warning,
52605267
* but still perform the call. */
52615268
zend_param_must_be_ref(EX(call)->func, arg_num);
5269+
must_wrap = 1;
52625270
}
52635271
}
52645272
} else {
@@ -5269,7 +5277,11 @@ ZEND_VM_C_LABEL(send_array):
52695277
}
52705278
}
52715279

5272-
ZVAL_COPY(param, arg);
5280+
if (EXPECTED(!must_wrap)) {
5281+
ZVAL_COPY(param, arg);
5282+
} else {
5283+
ZVAL_NEW_REF(param, arg);
5284+
}
52735285
if (!name) {
52745286
ZEND_CALL_NUM_ARGS(EX(call))++;
52755287
arg_num++;

Zend/zend_vm_execute.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
20982098
arg_num = 1;
20992099
param = ZEND_CALL_ARG(EX(call), 1);
21002100
ZEND_HASH_FOREACH_VAL(ht, arg) {
2101+
zend_bool must_wrap = 0;
21012102
if (skip > 0) {
21022103
skip--;
21032104
continue;
@@ -2109,6 +2110,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21092110
/* By-value send is not allowed -- emit a warning,
21102111
* but still perform the call. */
21112112
zend_param_must_be_ref(EX(call)->func, arg_num);
2113+
must_wrap = 1;
21122114
}
21132115
}
21142116
} else {
@@ -2118,7 +2120,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21182120
arg = Z_REFVAL_P(arg);
21192121
}
21202122
}
2121-
ZVAL_COPY(param, arg);
2123+
if (EXPECTED(!must_wrap)) {
2124+
ZVAL_COPY(param, arg);
2125+
} else {
2126+
ZVAL_NEW_REF(param, arg);
2127+
}
21222128
ZEND_CALL_NUM_ARGS(EX(call))++;
21232129
arg_num++;
21242130
param++;
@@ -2148,12 +2154,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21482154
HANDLE_EXCEPTION();
21492155
}
21502156

2157+
zend_bool must_wrap = 0;
21512158
if (ARG_SHOULD_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
21522159
if (UNEXPECTED(!Z_ISREF_P(arg))) {
21532160
if (!ARG_MAY_BE_SENT_BY_REF(EX(call)->func, arg_num)) {
21542161
/* By-value send is not allowed -- emit a warning,
21552162
* but still perform the call. */
21562163
zend_param_must_be_ref(EX(call)->func, arg_num);
2164+
must_wrap = 1;
21572165
}
21582166
}
21592167
} else {
@@ -2164,7 +2172,11 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_SEND_ARRAY_SPEC_HANDLER(ZEND_O
21642172
}
21652173
}
21662174

2167-
ZVAL_COPY(param, arg);
2175+
if (EXPECTED(!must_wrap)) {
2176+
ZVAL_COPY(param, arg);
2177+
} else {
2178+
ZVAL_NEW_REF(param, arg);
2179+
}
21682180
if (!name) {
21692181
ZEND_CALL_NUM_ARGS(EX(call))++;
21702182
arg_num++;

0 commit comments

Comments
 (0)