diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 342fe6fb5ba98..2a48b9c3713bb 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -8783,6 +8783,9 @@ ZEND_VM_HANDLER(158, ZEND_CALL_TRAMPOLINE, ANY, ANY, SPEC(OBSERVER)) EG(current_execute_data) = call->prev_execute_data; zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } if (ret == &retval) { zval_ptr_dtor(ret); } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 7387164a6d3d4..3bc01a597fa6c 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -3457,6 +3457,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_HANDLER(Z EG(current_execute_data) = call->prev_execute_data; zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } if (ret == &retval) { zval_ptr_dtor(ret); } @@ -3598,6 +3601,9 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CALL_TRAMPOLINE_SPEC_OBSERVER_ EG(current_execute_data) = call->prev_execute_data; zend_vm_stack_free_args(call); + if (UNEXPECTED(call_info & ZEND_CALL_HAS_EXTRA_NAMED_PARAMS)) { + zend_free_extra_named_params(call->extra_named_params); + } if (ret == &retval) { zval_ptr_dtor(ret); } diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 41cca5ae6e61a..901cd8e3063da 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -65,6 +65,7 @@ static zend_class_entry *zend_test_ns2_ns_foo_class; static zend_class_entry *zend_test_unit_enum; static zend_class_entry *zend_test_string_enum; static zend_class_entry *zend_test_int_enum; +static zend_class_entry *zend_test_magic_call; static zend_object_handlers zend_test_class_handlers; static ZEND_FUNCTION(zend_test_func) @@ -802,6 +803,24 @@ static ZEND_METHOD(ZendTestForbidDynamicCall, callStatic) zend_forbid_dynamic_call(); } +static ZEND_METHOD(_ZendTestMagicCall, __call) +{ + zend_string *name; + zval *arguments; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(name) + Z_PARAM_ARRAY(arguments) + ZEND_PARSE_PARAMETERS_END(); + + zval name_zv; + ZVAL_STR(&name_zv, name); + + zend_string_addref(name); + Z_TRY_ADDREF_P(arguments); + RETURN_ARR(zend_new_pair(&name_zv, arguments)); +} + PHP_INI_BEGIN() STD_PHP_INI_BOOLEAN("zend_test.replace_zend_execute_ex", "0", PHP_INI_SYSTEM, OnUpdateBool, replace_zend_execute_ex, zend_zend_test_globals, zend_test_globals) STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals) @@ -914,6 +933,8 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_string_enum = register_class_ZendTestStringEnum(); zend_test_int_enum = register_class_ZendTestIntEnum(); + zend_test_magic_call = register_class__ZendTestMagicCall(); + zend_register_functions(NULL, ext_function_legacy, NULL, EG(current_module)->type); // Loading via dl() not supported with the observer API diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index d877dac726d52..cf8ac50bd0629 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -47,6 +47,11 @@ public function returnsThrowable(): Throwable {} static public function variadicTest(string|Iterator ...$elements) : static {} } + class _ZendTestMagicCall + { + public function __call(string $name, array $args): mixed {} + } + class _ZendTestChildClass extends _ZendTestClass { public function returnsThrowable(): Exception {} diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 34f56f3d95040..70439ff726164 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a272e1920e36041da1c20eaf3afe1d6032164d24 */ + * Stub hash: 420711ec6f040d38bde450a169bf1186f8531191 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -142,6 +142,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestClass_variadicTes ZEND_ARG_VARIADIC_OBJ_TYPE_MASK(0, elements, Iterator, MAY_BE_STRING) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class__ZendTestMagicCall___call, 0, 2, IS_MIXED, 0) + ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, args, IS_ARRAY, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class__ZendTestChildClass_returnsThrowable, 0, 0, Exception, 0) ZEND_END_ARG_INFO() @@ -220,6 +225,7 @@ static ZEND_METHOD(_ZendTestClass, __toString); static ZEND_METHOD(_ZendTestClass, returnsStatic); static ZEND_METHOD(_ZendTestClass, returnsThrowable); static ZEND_METHOD(_ZendTestClass, variadicTest); +static ZEND_METHOD(_ZendTestMagicCall, __call); static ZEND_METHOD(_ZendTestChildClass, returnsThrowable); static ZEND_METHOD(_ZendTestTrait, testMethod); static ZEND_METHOD(ZendTestParameterAttribute, __construct); @@ -296,6 +302,12 @@ static const zend_function_entry class__ZendTestClass_methods[] = { }; +static const zend_function_entry class__ZendTestMagicCall_methods[] = { + ZEND_ME(_ZendTestMagicCall, __call, arginfo_class__ZendTestMagicCall___call, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + static const zend_function_entry class__ZendTestChildClass_methods[] = { ZEND_ME(_ZendTestChildClass, returnsThrowable, arginfo_class__ZendTestChildClass_returnsThrowable, ZEND_ACC_PUBLIC) ZEND_FE_END @@ -486,6 +498,16 @@ static zend_class_entry *register_class__ZendTestClass(zend_class_entry *class_e return class_entry; } +static zend_class_entry *register_class__ZendTestMagicCall(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "_ZendTestMagicCall", class__ZendTestMagicCall_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + return class_entry; +} + static zend_class_entry *register_class__ZendTestChildClass(zend_class_entry *class_entry__ZendTestClass) { zend_class_entry ce, *class_entry; diff --git a/ext/zend_test/tests/internal_magic_call.phpt b/ext/zend_test/tests/internal_magic_call.phpt new file mode 100644 index 0000000000000..2139ddeb5004a --- /dev/null +++ b/ext/zend_test/tests/internal_magic_call.phpt @@ -0,0 +1,23 @@ +--TEST-- +GH-12835: call->extra_named_params leaks on internal __call +--EXTENSIONS-- +zend_test +--FILE-- +test('a', 'b', c: 'c')); +?> +--EXPECT-- +array(2) { + [0]=> + string(4) "test" + [1]=> + array(3) { + [0]=> + string(1) "a" + [1]=> + string(1) "b" + ["c"]=> + string(1) "c" + } +}