Skip to content

Commit bbfdc17

Browse files
committed
Fix memory leak of function attribute hash table
==109253== 280 (56 direct, 224 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 4 ==109253== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so) ==109253== by 0x6D9FA2: __zend_malloc (zend_alloc.c:3068) ==109253== by 0x745138: zend_add_attribute (zend_attributes.c:226) ==109253== by 0x6680D1: zend_add_parameter_attribute (zend_attributes.h:102) ==109253== by 0x66B787: zm_startup_zend_test (test.c:478) ==109253== by 0x7224CD: zend_startup_module_ex (zend_API.c:2202) ==109253== by 0x72252C: zend_startup_module_zval (zend_API.c:2217) ==109253== by 0x734288: zend_hash_apply (zend_hash.c:2011) ==109253== by 0x722C30: zend_startup_modules (zend_API.c:2328) ==109253== by 0x67409B: php_module_startup (main.c:2256) ==109253== by 0x88EDDE: php_cli_startup (php_cli.c:409) ==109253== by 0x890F61: main (php_cli.c:1334)
1 parent 5e8adfb commit bbfdc17

File tree

5 files changed

+244
-5
lines changed

5 files changed

+244
-5
lines changed

Zend/zend_opcode.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ ZEND_API void zend_function_dtor(zval *zv)
156156
/* For methods this will be called explicitly. */
157157
if (!function->common.scope) {
158158
zend_free_internal_arg_info(&function->internal_function);
159+
160+
if (function->common.attributes) {
161+
zend_hash_release(function->common.attributes);
162+
function->common.attributes = NULL;
163+
}
159164
}
160165

161166
if (!(function->common.fn_flags & ZEND_ACC_ARENA_ALLOCATED)) {
@@ -433,11 +438,17 @@ ZEND_API void destroy_zend_class(zval *zv)
433438
zend_hash_destroy(&ce->properties_info);
434439
zend_string_release_ex(ce->name, 1);
435440

436-
/* TODO: eliminate this loop for classes without functions with arg_info */
441+
/* TODO: eliminate this loop for classes without functions with arg_info / attributes */
437442
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, fn) {
438-
if ((fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) &&
439-
fn->common.scope == ce) {
440-
zend_free_internal_arg_info(&fn->internal_function);
443+
if (fn->common.scope == ce) {
444+
if (fn->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS)) {
445+
zend_free_internal_arg_info(&fn->internal_function);
446+
}
447+
448+
if (fn->common.attributes) {
449+
zend_hash_release(fn->common.attributes);
450+
fn->common.attributes = NULL;
451+
}
441452
}
442453
} ZEND_HASH_FOREACH_END();
443454

ext/zend_test/test.c

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ static zend_class_entry *zend_test_class;
3737
static zend_class_entry *zend_test_child_class;
3838
static zend_class_entry *zend_test_trait;
3939
static zend_class_entry *zend_test_attribute;
40+
static zend_class_entry *zend_test_parameter_attribute;
41+
static zend_class_entry *zend_test_class_with_method_with_parameter_attribute;
42+
static zend_class_entry *zend_test_child_class_with_method_with_parameter_attribute;
4043
static zend_class_entry *zend_test_ns_foo_class;
4144
static zend_class_entry *zend_test_ns2_foo_class;
4245
static zend_class_entry *zend_test_ns2_ns_foo_class;
@@ -313,6 +316,17 @@ static ZEND_FUNCTION(namespaced_func)
313316
RETURN_TRUE;
314317
}
315318

319+
static ZEND_FUNCTION(zend_test_parameter_with_attribute)
320+
{
321+
zend_string *parameter;
322+
323+
ZEND_PARSE_PARAMETERS_START(1, 1)
324+
Z_PARAM_STR(parameter)
325+
ZEND_PARSE_PARAMETERS_END();
326+
327+
RETURN_LONG(1);
328+
}
329+
316330
static zend_object *zend_test_class_new(zend_class_entry *class_type)
317331
{
318332
zend_object *obj = zend_objects_new(class_type);
@@ -425,6 +439,39 @@ static ZEND_METHOD(ZendTestNS2_ZendSubNS_Foo, method)
425439
ZEND_PARSE_PARAMETERS_NONE();
426440
}
427441

442+
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override)
443+
{
444+
zend_string *parameter;
445+
446+
ZEND_PARSE_PARAMETERS_START(1, 1)
447+
Z_PARAM_STR(parameter)
448+
ZEND_PARSE_PARAMETERS_END();
449+
450+
RETURN_LONG(2);
451+
}
452+
453+
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, override)
454+
{
455+
zend_string *parameter;
456+
457+
ZEND_PARSE_PARAMETERS_START(1, 1)
458+
Z_PARAM_STR(parameter)
459+
ZEND_PARSE_PARAMETERS_END();
460+
461+
RETURN_LONG(3);
462+
}
463+
464+
static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override)
465+
{
466+
zend_string *parameter;
467+
468+
ZEND_PARSE_PARAMETERS_START(1, 1)
469+
Z_PARAM_STR(parameter)
470+
ZEND_PARSE_PARAMETERS_END();
471+
472+
RETURN_LONG(4);
473+
}
474+
428475
PHP_INI_BEGIN()
429476
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)
430477
STD_PHP_INI_BOOLEAN("zend_test.register_passes", "0", PHP_INI_SYSTEM, OnUpdateBool, register_passes, zend_zend_test_globals, zend_test_globals)
@@ -460,6 +507,40 @@ PHP_MINIT_FUNCTION(zend_test)
460507
attr->validator = zend_attribute_validate_zendtestattribute;
461508
}
462509

510+
zend_test_parameter_attribute = register_class_ZendTestParameterAttribute();
511+
zend_internal_attribute_register(zend_test_parameter_attribute, ZEND_ATTRIBUTE_TARGET_PARAMETER);
512+
513+
zend_add_parameter_attribute(
514+
zend_hash_str_find_ptr(CG(function_table), "zend_test_parameter_with_attribute", sizeof("zend_test_parameter_with_attribute") - 1),
515+
0,
516+
zend_test_parameter_attribute->name,
517+
0
518+
);
519+
520+
zend_test_class_with_method_with_parameter_attribute = register_class_ZendTestClassWithMethodWithParameterAttribute();
521+
522+
zend_add_parameter_attribute(
523+
zend_hash_str_find_ptr(&zend_test_class_with_method_with_parameter_attribute->function_table, "no_override", sizeof("no_override") - 1),
524+
0,
525+
zend_test_parameter_attribute->name,
526+
0
527+
);
528+
zend_add_parameter_attribute(
529+
zend_hash_str_find_ptr(&zend_test_class_with_method_with_parameter_attribute->function_table, "override", sizeof("override") - 1),
530+
0,
531+
zend_test_parameter_attribute->name,
532+
0
533+
);
534+
535+
zend_test_child_class_with_method_with_parameter_attribute = register_class_ZendTestChildClassWithMethodWithParameterAttribute(zend_test_class_with_method_with_parameter_attribute);
536+
537+
zend_add_parameter_attribute(
538+
zend_hash_str_find_ptr(&zend_test_class_with_method_with_parameter_attribute->function_table, "override", sizeof("override") - 1),
539+
0,
540+
zend_test_parameter_attribute->name,
541+
0
542+
);
543+
463544
zend_test_ns_foo_class = register_class_ZendTestNS_Foo();
464545
zend_test_ns2_foo_class = register_class_ZendTestNS2_Foo();
465546
zend_test_ns2_ns_foo_class = register_class_ZendTestNS2_ZendSubNS_Foo();

ext/zend_test/test.stub.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,19 @@ final class ZendTestAttribute {
4545

4646
}
4747

48+
final class ZendTestParameterAttribute {
49+
50+
}
51+
52+
class ZendTestClassWithMethodWithParameterAttribute {
53+
final public function no_override(#[ZendTestParameterAttribute] string $parameter): int {}
54+
public function override(#[ZendTestParameterAttribute] string $parameter): int {}
55+
}
56+
57+
class ZendTestChildClassWithMethodWithParameterAttribute extends ZendTestClassWithMethodWithParameterAttribute {
58+
public function override(#[ZendTestParameterAttribute] string $parameter): int {}
59+
}
60+
4861
enum ZendTestUnitEnum {
4962
case Foo;
5063
case Bar;
@@ -92,6 +105,8 @@ function zend_weakmap_remove(object $object): bool {}
92105
function zend_weakmap_dump(): array {}
93106

94107
function zend_get_unit_enum(): ZendTestUnitEnum {}
108+
109+
function zend_test_parameter_with_attribute(#[ZendTestParameterAttribute] string $parameter): int {}
95110
}
96111

97112
namespace ZendTestNS {

ext/zend_test/test_arginfo.h

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 7326163f8ce5340c12e74af72d47a8926eb39786 */
2+
* Stub hash: 068310d46bc9f5454a2f47e6a7ff52c9ea3f0b22 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
55
ZEND_END_ARG_INFO()
@@ -71,6 +71,10 @@ ZEND_END_ARG_INFO()
7171
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_zend_get_unit_enum, 0, 0, ZendTestUnitEnum, 0)
7272
ZEND_END_ARG_INFO()
7373

74+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_parameter_with_attribute, 0, 1, IS_LONG, 0)
75+
ZEND_ARG_TYPE_INFO(0, parameter, IS_STRING, 0)
76+
ZEND_END_ARG_INFO()
77+
7478
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ZendTestNS2_ZendSubNS_namespaced_func, 0, 0, _IS_BOOL, 0)
7579
ZEND_END_ARG_INFO()
7680

@@ -91,6 +95,12 @@ ZEND_END_ARG_INFO()
9195

9296
#define arginfo_class__ZendTestTrait_testMethod arginfo_ZendTestNS2_ZendSubNS_namespaced_func
9397

98+
#define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override arginfo_zend_test_parameter_with_attribute
99+
100+
#define arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override arginfo_zend_test_parameter_with_attribute
101+
102+
#define arginfo_class_ZendTestChildClassWithMethodWithParameterAttribute_override arginfo_zend_test_parameter_with_attribute
103+
94104
#define arginfo_class_ZendTestNS_Foo_method arginfo_zend_test_void_return
95105

96106
#define arginfo_class_ZendTestNS2_Foo_method arginfo_zend_test_void_return
@@ -116,13 +126,17 @@ static ZEND_FUNCTION(zend_weakmap_attach);
116126
static ZEND_FUNCTION(zend_weakmap_remove);
117127
static ZEND_FUNCTION(zend_weakmap_dump);
118128
static ZEND_FUNCTION(zend_get_unit_enum);
129+
static ZEND_FUNCTION(zend_test_parameter_with_attribute);
119130
static ZEND_FUNCTION(namespaced_func);
120131
static ZEND_METHOD(_ZendTestClass, is_object);
121132
static ZEND_METHOD(_ZendTestClass, __toString);
122133
static ZEND_METHOD(_ZendTestClass, returnsStatic);
123134
static ZEND_METHOD(_ZendTestClass, returnsThrowable);
124135
static ZEND_METHOD(_ZendTestChildClass, returnsThrowable);
125136
static ZEND_METHOD(_ZendTestTrait, testMethod);
137+
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, no_override);
138+
static ZEND_METHOD(ZendTestClassWithMethodWithParameterAttribute, override);
139+
static ZEND_METHOD(ZendTestChildClassWithMethodWithParameterAttribute, override);
126140
static ZEND_METHOD(ZendTestNS_Foo, method);
127141
static ZEND_METHOD(ZendTestNS2_Foo, method);
128142
static ZEND_METHOD(ZendTestNS2_ZendSubNS_Foo, method);
@@ -147,6 +161,7 @@ static const zend_function_entry ext_functions[] = {
147161
ZEND_FE(zend_weakmap_remove, arginfo_zend_weakmap_remove)
148162
ZEND_FE(zend_weakmap_dump, arginfo_zend_weakmap_dump)
149163
ZEND_FE(zend_get_unit_enum, arginfo_zend_get_unit_enum)
164+
ZEND_FE(zend_test_parameter_with_attribute, arginfo_zend_test_parameter_with_attribute)
150165
ZEND_NS_FE("ZendTestNS2\\ZendSubNS", namespaced_func, arginfo_ZendTestNS2_ZendSubNS_namespaced_func)
151166
ZEND_FE_END
152167
};
@@ -183,6 +198,24 @@ static const zend_function_entry class_ZendTestAttribute_methods[] = {
183198
};
184199

185200

201+
static const zend_function_entry class_ZendTestParameterAttribute_methods[] = {
202+
ZEND_FE_END
203+
};
204+
205+
206+
static const zend_function_entry class_ZendTestClassWithMethodWithParameterAttribute_methods[] = {
207+
ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, no_override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_no_override, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL)
208+
ZEND_ME(ZendTestClassWithMethodWithParameterAttribute, override, arginfo_class_ZendTestClassWithMethodWithParameterAttribute_override, ZEND_ACC_PUBLIC)
209+
ZEND_FE_END
210+
};
211+
212+
213+
static const zend_function_entry class_ZendTestChildClassWithMethodWithParameterAttribute_methods[] = {
214+
ZEND_ME(ZendTestChildClassWithMethodWithParameterAttribute, override, arginfo_class_ZendTestChildClassWithMethodWithParameterAttribute_override, ZEND_ACC_PUBLIC)
215+
ZEND_FE_END
216+
};
217+
218+
186219
static const zend_function_entry class_ZendTestUnitEnum_methods[] = {
187220
ZEND_FE_END
188221
};
@@ -314,6 +347,37 @@ static zend_class_entry *register_class_ZendTestAttribute(void)
314347
return class_entry;
315348
}
316349

350+
static zend_class_entry *register_class_ZendTestParameterAttribute(void)
351+
{
352+
zend_class_entry ce, *class_entry;
353+
354+
INIT_CLASS_ENTRY(ce, "ZendTestParameterAttribute", class_ZendTestParameterAttribute_methods);
355+
class_entry = zend_register_internal_class_ex(&ce, NULL);
356+
class_entry->ce_flags |= ZEND_ACC_FINAL;
357+
358+
return class_entry;
359+
}
360+
361+
static zend_class_entry *register_class_ZendTestClassWithMethodWithParameterAttribute(void)
362+
{
363+
zend_class_entry ce, *class_entry;
364+
365+
INIT_CLASS_ENTRY(ce, "ZendTestClassWithMethodWithParameterAttribute", class_ZendTestClassWithMethodWithParameterAttribute_methods);
366+
class_entry = zend_register_internal_class_ex(&ce, NULL);
367+
368+
return class_entry;
369+
}
370+
371+
static zend_class_entry *register_class_ZendTestChildClassWithMethodWithParameterAttribute(zend_class_entry *class_entry_ZendTestClassWithMethodWithParameterAttribute)
372+
{
373+
zend_class_entry ce, *class_entry;
374+
375+
INIT_CLASS_ENTRY(ce, "ZendTestChildClassWithMethodWithParameterAttribute", class_ZendTestChildClassWithMethodWithParameterAttribute_methods);
376+
class_entry = zend_register_internal_class_ex(&ce, class_entry_ZendTestClassWithMethodWithParameterAttribute);
377+
378+
return class_entry;
379+
}
380+
317381
static zend_class_entry *register_class_ZendTestUnitEnum(void)
318382
{
319383
zend_class_entry *class_entry = zend_register_internal_enum("ZendTestUnitEnum", IS_UNDEF, class_ZendTestUnitEnum_methods);
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
--TEST--
2+
Verify that parameter attributes for native functions do not leak.
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
var_dump(zend_test_parameter_with_attribute("foo"));
9+
10+
$o = new ZendTestClassWithMethodWithParameterAttribute();
11+
var_dump($o->no_override("foo"));
12+
var_dump($o->override("foo"));
13+
14+
$o = new ZendTestChildClassWithMethodWithParameterAttribute();
15+
var_dump($o->no_override("foo"));
16+
var_dump($o->override("foo"));
17+
18+
class ChildClassWithNoAttribute extends ZendTestClassWithMethodWithParameterAttribute {
19+
public function override(string $parameter): int
20+
{
21+
return 5;
22+
}
23+
}
24+
25+
$o = new ChildClassWithNoAttribute();
26+
var_dump($o->no_override("foo"));
27+
var_dump($o->override("foo"));
28+
29+
class ChildClassWithSameAttribute extends ZendTestClassWithMethodWithParameterAttribute {
30+
public function override(#[ZendTestParameterAttribute] string $parameter): int
31+
{
32+
return 6;
33+
}
34+
}
35+
36+
$o = new ChildClassWithSameAttribute();
37+
var_dump($o->no_override("foo"));
38+
var_dump($o->override("foo"));
39+
40+
#[\Attribute(\Attribute::TARGET_PARAMETER)]
41+
class SomeAttribute {
42+
43+
}
44+
45+
class ChildClassWithDifferentAttribute extends ZendTestClassWithMethodWithParameterAttribute {
46+
public function override(#[SomeAttribute] string $parameter): int
47+
{
48+
return 7;
49+
}
50+
}
51+
52+
$o = new ChildClassWithDifferentAttribute();
53+
var_dump($o->no_override("foo"));
54+
var_dump($o->override("foo"));
55+
56+
?>
57+
--EXPECT--
58+
int(1)
59+
int(2)
60+
int(3)
61+
int(2)
62+
int(4)
63+
int(2)
64+
int(5)
65+
int(2)
66+
int(6)
67+
int(2)
68+
int(7)

0 commit comments

Comments
 (0)