Fix leak of call->extra_named_params on internal __call

Fixes GH-12835
Closes GH-12836
This commit is contained in:
Ilija Tovilo 2023-11-30 13:41:07 +01:00
parent 2303e76740
commit f203edd3c5
No known key found for this signature in database
GPG Key ID: A4F5D403F118200A
7 changed files with 83 additions and 1 deletions

2
NEWS
View File

@ -10,6 +10,8 @@ PHP NEWS
. Fixed bug GH-12758 / GH-12768 (Invalid opline in OOM handlers within
ZEND_FUNC_GET_ARGS and ZEND_BIND_STATIC). (Florian Engelhardt)
. Fix various missing NULL checks. (nielsdos, dstogov)
. Fixed bug GH-12835 (Leak of call->extra_named_params on internal __call).
(ilutov)
- Date:
. Fixed improbably integer overflow while parsing really large (or small)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -47,6 +47,11 @@ namespace {
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 {}

View File

@ -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;

View File

@ -0,0 +1,23 @@
--TEST--
GH-12835: call->extra_named_params leaks on internal __call
--EXTENSIONS--
zend_test
--FILE--
<?php
$obj = new _ZendTestMagicCall;
var_dump($obj->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"
}
}