diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 733ce54dc24ad..c0097c55cbc48 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4688,6 +4688,44 @@ static const zend_live_range *find_live_range(const zend_op_array *op_array, uin } /* }}} */ +/* Replace optional parameters that weren't passed with their declared default values, + * which allows us to check that this does not change the behavior of the function. */ +#define ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS 1 +#if ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS +static void zend_verify_internal_param_defaults(zend_execute_data **call_ptr) { + zend_function *fbc = (*call_ptr)->func; + uint32_t num_passed_args = ZEND_CALL_NUM_ARGS(*call_ptr); + if (num_passed_args < fbc->common.required_num_args) { + /* This is an error anyway. */ + return; + } + + uint32_t num_declared_args = fbc->common.num_args; + while (num_passed_args < num_declared_args) { + zend_internal_arg_info *arg_info = &fbc->internal_function.arg_info[num_passed_args]; + zval default_value; + if (zend_get_default_from_internal_arg_info(&default_value, arg_info) == FAILURE) { + /* Default value not available, so we can't pass any further defaults either. */ + return; + } + + if (Z_TYPE(default_value) == IS_CONSTANT_AST) { + zval_update_constant_ex(&default_value, fbc->common.scope); + } + + zend_vm_stack_extend_call_frame(call_ptr, num_passed_args, 1); + zval *arg = ZEND_CALL_VAR_NUM(*call_ptr, num_passed_args); + ZVAL_COPY_VALUE(arg, &default_value); + if (ARG_SHOULD_BE_SENT_BY_REF(fbc, num_passed_args + 1)) { + ZVAL_MAKE_REF(arg); + } + + num_passed_args++; + ZEND_CALL_NUM_ARGS(*call_ptr)++; + } +} +#endif + static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, uint32_t catch_op_num) /* {{{ */ { int i; diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 55fdb7d46582b..be3a17da5c96c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4046,6 +4046,9 @@ ZEND_VM_HOT_HANDLER(129, ZEND_DO_ICALL, ANY, ANY, SPEC(RETVAL,OBSERVER)) zval retval; SAVE_OPLINE(); +#if ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS +zend_verify_internal_param_defaults(&call); +#endif EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 16455b6e0cd58..55037e618ffe3 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -1272,6 +1272,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zval retval; SAVE_OPLINE(); +#if ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS +zend_verify_internal_param_defaults(&call); +#endif EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -1334,6 +1337,9 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_RETV zval retval; SAVE_OPLINE(); +#if ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS +zend_verify_internal_param_defaults(&call); +#endif EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data; @@ -1396,6 +1402,9 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_DO_ICALL_SPEC_OBS zval retval; SAVE_OPLINE(); +#if ZEND_VERIFY_INTERNAL_PARAM_DEFAULTS +zend_verify_internal_param_defaults(&call); +#endif EX(call) = call->prev_execute_data; call->prev_execute_data = execute_data;