8000 Zend: Fix reference counting for Closures in const-expr · TimWolla/php-src@c80c656 · GitHub
[go: up one dir, main page]

Skip to content

Commit c80c656

Browse files
committed
Zend: Fix reference counting for Closures in const-expr
Fixes php#17851
1 parent da1e254 commit c80c656

File tree

4 files changed

+72
-3
lines changed

4 files changed

+72
-3
lines changed

Zend/tests/closures/closure_const_expr/attributes.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ reflection
88
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
99
class Attr {
1010
public function __construct(public Closure $value) {
11-
$value('foo');
11+
$value('foo');
1212
}
1313
}
1414

1515
#[Attr(static function () { })]
1616
#[Attr(static function (...$args) {
17-
var_dump($args);
17+
var_dump($args);
1818
})]
1919
class C {}
2020

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
#[Attr(static function () { })]
4+
#[Attr(static function (...$args) {
5+
var_dump($args);
6+
})]
7+
class C {}
8+
9+
?>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
--TEST--
2+
GH-17851: Use-after-free when instantiating autoloaded class with attribute having a Closure parameter.
3+
--EXTENSIONS--
4+
reflection
5+
--FILE--
6+
<?php
7+
8+
spl_autoload_register(static function ($className) {
9+
if ($className === 'C') {
10+
require(__DIR__ . '/attributes_autoload.inc');
11+
}
12+
});
13+
14+
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
15+
class Attr {
16+
public function __construct(public Closure $value) {
17+
$value('foo');
18+
}
19+
}
20+
21+
foreach ((new ReflectionClass(C::class))->getAttributes() as $reflectionAttribute) {
22+
var_dump($reflectionAttribute->newInstance());
23+
}
24+
25+
?>
26+
--EXPECTF--
27+
object(Attr)#%d (1) {
28+
["value"]=>
29+
object(Closure)#%d (3) {
30+
["name"]=>
31+
string(%d) "{closure:%s:%d}"
32+
["file"]=>
33+
string(%d) "%s"
34+
["line"]=>
35+
int(%d)
36+
}
37+
}
38+
array(1) {
39+
[0]=>
40+
string(3) "foo"
41+
}
42+
object(Attr)#%d (1) {
43+
["value"]=>
44+
object(Closure)#%d (4) {
45+
["name"]=>
46+
string(%d) "{closure:%s:%d}"
47+
["file"]=>
48+
string(%d) "%s"
49+
["line"]=>
50+
int(%d)
51+
["parameter"]=>
52+
array(1) {
53+
["$args"]=>
54+
string(10) "<optional>"
55+
}
56+
}
57+
}

Zend/zend_ast.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_arr
120120
ast->attr = 0;
121121
ast->lineno = CG(zend_lineno);
122122
ast->op_array = op_array;
123+
function_add_ref((zend_function *)op_array);
123124

124125
return (zend_ast *) ast;
125126
}
@@ -1277,6 +1278,7 @@ static void* ZEND_FASTCALL zend_ast_tree_copy(zend_ast *ast, void *buf)
12771278
new->attr = old->attr;
12781279
new->lineno = old->lineno;
12791280
new->op_array = old->op_array;
1281+
function_add_ref((zend_function *)new->op_array);
12801282
buf = (void*)((char*)buf + sizeof(zend_ast_op_array));
12811283
} else if (ast->kind == ZEND_AST_CALLABLE_CONVERT) {
12821284
zend_ast_fcc *old = (zend_ast_fcc*)ast;
@@ -1353,7 +1355,8 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
13531355
} else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) {
13541356
zend_string_release_ex(zend_ast_get_constant_name(ast), 0);
13551357
} else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) {
1356-
/* Nothing to do. */
1358+
zend_ast_op_array *op_array = zend_ast_get_op_array(ast);
1359+
destroy_op_array(op_array->op_array);
13571360
} else if (EXPECTED(zend_ast_is_decl(ast))) {
13581361
zend_ast_decl *decl = (zend_ast_decl *) ast;
13591362

0 commit comments

Comments
 (0)
0