Skip to content

Commit fb1ec9a

Browse files
committed
Fix uncatchable exception thrown in generator
This procedure may be called during i_free_compiled_variables(), when EG(current_execute_data) is unfortunately already reset to the parent frame. EG(opline_before_exception) does not actually belong to this frame. Furthermore, setting opline to EG(exception_op) early will miss a later zend_rethrow_exception(), which will also miss installation of the correct EG(opline_before_exception). Fixes GH-20714 Closes GH-20716
1 parent e776695 commit fb1ec9a

File tree

3 files changed

+34
-2
lines changed

3 files changed

+34
-2
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PHP NEWS
77
with dynamic class const lookup default argument). (ilutov)
88
. Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing
99
malformed INI input via parse_ini_string()). (ndossche)
10+
. Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov)
1011

1112
- Bz2:
1213
. Fixed bug GH-20620 (bzcompress overflow on large source size).

Zend/tests/gh20714.phpt

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
--TEST--
2+
GH-20714: Uncatchable exception thrown in generator
3+
--CREDITS--
4+
Grégoire Paris (greg0ire)
5+
--FILE--
6+
<?php
7+
8+
function gen(): Generator {
9+
try {
10+
yield 1;
11+
} finally {}
12+
}
13+
14+
function process(): void {
15+
$g = gen();
16+
foreach ($g as $_) {
17+
throw new Exception('ERROR');
18+
}
19+
}
20+
21+
try {
22+
process();
23+
} catch (Exception $e) {
24+
echo "Caught\n";
25+
}
26+
27+
?>
28+
--EXPECT--
29+
Caught

Zend/zend_generators.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,9 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
321321
zend_object *old_exception = NULL;
322322
const zend_op *old_opline_before_exception = NULL;
323323
if (EG(exception)) {
324-
if (EG(current_execute_data)) {
324+
if (EG(current_execute_data)
325+
&& EG(current_execute_data)->opline
326+
&& EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) {
325327
EG(current_execute_data)->opline = EG(opline_before_exception);
326328
old_opline_before_exception = EG(opline_before_exception);
327329
}
@@ -337,7 +339,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */
337339
zend_generator_resume(generator);
338340

339341
if (old_exception) {
340-
if (EG(current_execute_data)) {
342+
if (old_opline_before_exception) {
341343
EG(current_execute_data)->opline = EG(exception_op);
342344
EG(opline_before_exception) = old_opline_before_exception;
343345
}

0 commit comments

Comments
 (0)