From: Christoph M. Becker Date: Mon, 24 Aug 2020 13:02:15 +0000 (+0200) Subject: Merge branch 'PHP-7.4' into master X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5643f34a1ed71ae33d313e31ebb8c814cc8e3c0a;p=php Merge branch 'PHP-7.4' into master * PHP-7.4: Fix #79979: passing value to by-ref param via CUFA crashes --- 5643f34a1ed71ae33d313e31ebb8c814cc8e3c0a diff --cc Zend/tests/bug79979.phpt index 0000000000,8d7f30b7c8..c41d3889e9 mode 000000,100644..100644 --- a/Zend/tests/bug79979.phpt +++ b/Zend/tests/bug79979.phpt @@@ -1,0 -1,17 +1,17 @@@ + --TEST-- + Bug #79979 (passing value to by-ref param via CUF(A) crashes) + --FILE-- + + --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: 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 diff --cc Zend/zend_execute_API.c index b3d6319560,78a041d6ce..ece3679822 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@@ -732,24 -755,26 +732,26 @@@ int zend_call_function(zend_fcall_info } for (i=0; iparam_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) { @@@ -767,61 -792,14 +769,71 @@@ } } - 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); + } } + 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, - * 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; + } + } + } + } 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); + } + } + - 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++; + } + } ZEND_HASH_FOREACH_END(); + } + if (UNEXPECTED(func->op_array.fn_flags & ZEND_ACC_CLOSURE)) { uint32_t call_info; diff --cc Zend/zend_vm_def.h index b92928e3b0,fa70bf9f96..805f8f0a3e --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@@ -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)) { @@@ -5268,13 -5183,14 +5276,17 @@@ 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++; + if (!name) { + ZEND_CALL_NUM_ARGS(EX(call))++; + arg_num++; + param++; + } } ZEND_HASH_FOREACH_END(); } } diff --cc Zend/zend_vm_execute.h index 7a50d99d29,02b0ff24d1..d87edfc570 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@@ -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)) { @@@ -2163,13 -2127,14 +2171,17 @@@ 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++; + if (!name) { + ZEND_CALL_NUM_ARGS(EX(call))++; + arg_num++; + param++; + } } ZEND_HASH_FOREACH_END(); } }