--- /dev/null
-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
}
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;
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();
}
}
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();
}
}