Skip to content

Commit 2480362

Browse files
committed
Root arguments of internal frames
1 parent 8547fba commit 2480362

File tree

3 files changed

+112
-4
lines changed

3 files changed

+112
-4
lines changed

Zend/tests/gc/gh13687-003.phpt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
GH-13687 003: Result operand may leak if GC is triggered before consumption
3+
--FILE--
4+
<?php
5+
6+
class A {
7+
public $cycle;
8+
public function __construct() { $this->cycle = $this; }
9+
}
10+
class B {
11+
public function get() {
12+
return new A();
13+
}
14+
}
15+
class Debug {
16+
public function __debugInfo() {
17+
gc_collect_cycles();
18+
return [];
19+
}
20+
}
21+
22+
$c = new B();
23+
var_dump($c->get(), new Debug);
24+
25+
?>
26+
==DONE==
27+
--EXPECTF--
28+
object(A)#%d (1) {
29+
["cycle"]=>
30+
*RECURSION*
31+
}
32+
object(Debug)#%d (0) {
33+
}
34+
==DONE==

Zend/tests/gc/gh13687-004.phpt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-13687 004: Result operand may leak if GC is triggered before consumption
3+
--ENV--
4+
func=call_user_func
5+
--FILE--
6+
<?php
7+
8+
class A {
9+
public $cycle;
10+
public function __construct() { $this->cycle = $this; }
11+
}
12+
class B {
13+
public function get() {
14+
return new A();
15+
}
16+
}
17+
class Debug {
18+
public function __debugInfo() {
19+
gc_collect_cycles();
20+
return [];
21+
}
22+
}
23+
24+
$c = new B();
25+
getenv('func')(fn (...$args) => gc_collect_cycles(), a: $c->get());
26+
27+
?>
28+
==DONE==
29+
--EXPECT--
30+
==DONE==

Zend/zend_gc.c

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2221,7 +2221,7 @@ static void zend_gc_check_root_tmpvars(void) {
22212221
return;
22222222
}
22232223

2224-
if (ZEND_USER_CODE(ex->func->type)) {
2224+
if (ex->func && ZEND_USER_CODE(ex->func->type)) {
22252225
const zend_op *op = ex->opline;
22262226
if (op->result_type & (IS_VAR | IS_TMP_VAR)) {
22272227
switch (op->opcode) {
@@ -2249,7 +2249,29 @@ static void zend_gc_check_root_tmpvars(void) {
22492249

22502250
for (; ex; ex = ex->prev_execute_data) {
22512251
zend_function *func = ex->func;
2252-
if (!func || !ZEND_USER_CODE(func->type)) {
2252+
if (!func) {
2253+
continue;
2254+
}
2255+
2256+
if (!ZEND_USER_CODE(func->type)) {
2257+
/* For internal frames, arguments are not consumed yet, so we must
2258+
* root them. */
2259+
zend_execute_data *caller = ex->prev_execute_data;
2260+
if (!caller->func || !ZEND_USER_CODE(caller->func->type)) {
2261+
continue;
2262+
}
2263+
2264+
uint32_t num_args = ZEND_CALL_NUM_ARGS(ex);
2265+
if (EXPECTED(num_args > 0)) {
2266+
zval *p = ZEND_CALL_ARG(ex, 1);
2267+
do {
2268+
if (Z_COLLECTABLE_P(p)) {
2269+
gc_check_possible_root(Z_COUNTED_P(p));
2270+
}
2271+
p++;
2272+
} while (--num_args);
2273+
}
2274+
22532275
continue;
22542276
}
22552277

@@ -2282,7 +2304,7 @@ static void zend_gc_remove_root_tmpvars(void) {
22822304
return;
22832305
}
22842306

2285-
if (ZEND_USER_CODE(ex->func->type)) {
2307+
if (ex->func && ZEND_USER_CODE(ex->func->type)) {
22862308
const zend_op *op = ex->opline;
22872309
if (op->result_type & (IS_VAR | IS_TMP_VAR)) {
22882310
switch (op->opcode) {
@@ -2310,7 +2332,29 @@ static void zend_gc_remove_root_tmpvars(void) {
23102332

23112333
for (; ex; ex = ex->prev_execute_data) {
23122334
zend_function *func = ex->func;
2313-
if (!func || !ZEND_USER_CODE(func->type)) {
2335+
if (!func) {
2336+
continue;
2337+
}
2338+
2339+
if (!ZEND_USER_CODE(func->type)) {
2340+
/* For internal frames, arguments are live, so we should unroot
2341+
* them. */
2342+
zend_execute_data *caller = ex->prev_execute_data;
2343+
if (!caller->func || !ZEND_USER_CODE(caller->func->type)) {
2344+
continue;
2345+
}
2346+
2347+
uint32_t num_args = ZEND_CALL_NUM_ARGS(ex);
2348+
if (EXPECTED(num_args > 0)) {
2349+
zval *p = ZEND_CALL_ARG(ex, 1);
2350+
do {
2351+
if (Z_COLLECTABLE_P(p)) {
2352+
GC_REMOVE_FROM_BUFFER(Z_COUNTED_P(p));
2353+
}
2354+
p++;
2355+
} while (--num_args);
2356+
}
2357+
23142358
continue;
23152359
}
23162360

0 commit comments

Comments
 (0)