Skip to content

Commit 75620c6

Browse files
committed
Fix printing backtrace during generator closing
Fixes GH-15851
1 parent 422aa17 commit 75620c6

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

Zend/tests/generators/gh15851.phpt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
GH-15851: Access on NULL when printing backtrace with freed generator
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public function __destruct() {
8+
debug_print_backtrace();
9+
}
10+
}
11+
12+
function bar() {
13+
yield from foo();
14+
}
15+
16+
function foo() {
17+
$foo = new Foo();
18+
yield;
19+
}
20+
21+
$gen = bar();
22+
foreach ($gen as $dummy);
23+
24+
?>
25+
--EXPECTF--
26+
#0 %s(%d): Foo->__destruct()
27+
#1 %s(%d): bar()

Zend/zend_builtin_functions.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,16 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int
17251725
}
17261726

17271727
while (call && (limit == 0 || frameno < limit)) {
1728+
if (UNEXPECTED(!call->func)) {
1729+
/* This is the fake frame inserted for nested generators. Normally,
1730+
* this frame is preceded by the actual generator frame and then
1731+
* replaced by zend_generator_check_placeholder_frame() below.
1732+
* However, the frame is popped before cleaning the stack frame,
1733+
* which is observable by destructors. */
1734+
call = zend_generator_check_placeholder_frame(call);
1735+
ZEND_ASSERT(call->func);
1736+
}
1737+
17281738
zend_execute_data *prev = call->prev_execute_data;
17291739

17301740
if (!prev) {

0 commit comments

Comments
 (0)