Skip to content

Commit cd9d932

Browse files
committed
Free previous state when re-calling Reflection*::__construct()
ReflectionMethod::__construct() overwrote intern->ptr without releasing the previous Closure::__invoke() trampoline, and ReflectionClassConstant::__construct() overwrote the $name and $class slots without dropping their previous references. ReflectionFunction, ReflectionParameter and ReflectionProperty already release prior state on re-entry.
1 parent a96f6ff commit cd9d932

3 files changed

Lines changed: 72 additions & 0 deletions

File tree

ext/reflection/php_reflection.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,6 +3420,12 @@ static void instantiate_reflection_method(INTERNAL_FUNCTION_PARAMETERS, bool is_
34203420
}
34213421
efree(lcname);
34223422

3423+
if (intern->ptr) {
3424+
_free_function(intern->ptr);
3425+
zval_ptr_dtor(reflection_prop_name(object));
3426+
zval_ptr_dtor(reflection_prop_class(object));
3427+
}
3428+
34233429
ZVAL_STR_COPY(reflection_prop_name(object), mptr->common.function_name);
34243430
ZVAL_STR_COPY(reflection_prop_class(object), mptr->common.scope->name);
34253431
intern->ptr = mptr;
@@ -3932,6 +3938,11 @@ ZEND_METHOD(ReflectionClassConstant, __construct)
39323938
RETURN_THROWS();
39333939
}
39343940

3941+
if (intern->ptr) {
3942+
zval_ptr_dtor(reflection_prop_name(object));
3943+
zval_ptr_dtor(reflection_prop_class(object));
3944+
}
3945+
39353946
intern->ptr = constant;
39363947
intern->ref_type = REF_TYPE_CLASS_CONSTANT;
39373948
intern->ce = constant->ce;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
ReflectionClassConstant double construct call does not leak $name and $class
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
const FOO = 1;
8+
}
9+
10+
function test(ReflectionClassConstant $r) {
11+
/* implode() so that the name is not an interned string. */
12+
$r->__construct(C::class, implode('', ['F', 'O', 'O']));
13+
}
14+
15+
$r = new ReflectionClassConstant(C::class, 'FOO');
16+
for ($i = 0; $i < 10; $i++) {
17+
test($r);
18+
}
19+
20+
$before = memory_get_usage();
21+
for ($i = 0; $i < 1000; $i++) {
22+
test($r);
23+
}
24+
$after = memory_get_usage();
25+
26+
var_dump($before === $after);
27+
var_dump($r->name, $r->class);
28+
29+
?>
30+
--EXPECT--
31+
bool(true)
32+
string(3) "FOO"
33+
string(1) "C"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
--TEST--
2+
ReflectionMethod double construct call on Closure::__invoke() does not leak
3+
--FILE--
4+
<?php
5+
6+
function test(ReflectionMethod $r) {
7+
$r->__construct(function () {}, '__invoke');
8+
}
9+
10+
$r = new ReflectionMethod(function () {}, '__invoke');
11+
for ($i = 0; $i < 10; $i++) {
12+
test($r);
13+
}
14+
15+
$before = memory_get_usage();
16+
for ($i = 0; $i < 1000; $i++) {
17+
test($r);
18+
}
19+
$after = memory_get_usage();
20+
21+
var_dump($before === $after);
22+
var_dump($r->name, $r->class);
23+
24+
?>
25+
--EXPECT--
26+
bool(true)
27+
string(8) "__invoke"
28+
string(7) "Closure"

0 commit comments

Comments
 (0)