Skip to content

Commit 04f5a7c

Browse files
committed
Zend: Fix reference counting for Closures in const-expr
Fixes #17851
1 parent da38477 commit 04f5a7c

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_op_array(zend_op_array *op_arr
108108
ast->attr = 0;
109109
ast->lineno = CG(zend_lineno);
110110
ast->op_array = op_array;
111+
function_add_ref((zend_function *)op_array);
111112

112113
return (zend_ast *) ast;
113114
}
@@ -1226,7 +1227,8 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast)
12261227
} else if (EXPECTED(ast->kind == ZEND_AST_CONSTANT)) {
12271228
zend_string_release_ex(zend_ast_get_constant_name(ast), 0);
12281229
} else if (EXPECTED(ast->kind == ZEND_AST_OP_ARRAY)) {
1229-
/* Nothing to do. */
1230+
zend_ast_op_array *op_array = zend_ast_get_op_array(ast);
1231+
destroy_op_array(op_array->op_array);
12301232
} else if (EXPECTED(zend_ast_is_decl(ast))) {
12311233
zend_ast_decl *decl = (zend_ast_decl *) ast;
12321234

0 commit comments

Comments
 (0)