From fb1ec9a5a7c35e0bdb4aa5b793d4aac7bb765a9c Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 15 Dec 2025 23:39:07 +0100 Subject: [PATCH] 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 --- NEWS | 1 + Zend/tests/gh20714.phpt | 29 +++++++++++++++++++++++++++++ Zend/zend_generators.c | 6 ++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 Zend/tests/gh20714.phpt diff --git a/NEWS b/NEWS index beff3f224ce..11ae976b323 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ PHP NEWS with dynamic class const lookup default argument). (ilutov) . Fixed bug GH-20695 (Assertion failure in normalize_value() when parsing malformed INI input via parse_ini_string()). (ndossche) + . Fixed bug GH-20714 (Uncatchable exception thrown in generator). (ilutov) - Bz2: . Fixed bug GH-20620 (bzcompress overflow on large source size). diff --git a/Zend/tests/gh20714.phpt b/Zend/tests/gh20714.phpt new file mode 100644 index 00000000000..10ffde555f8 --- /dev/null +++ b/Zend/tests/gh20714.phpt @@ -0,0 +1,29 @@ +--TEST-- +GH-20714: Uncatchable exception thrown in generator +--CREDITS-- +Grégoire Paris (greg0ire) +--FILE-- + +--EXPECT-- +Caught diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index 5ba215f788c..cfadf06b46f 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -321,7 +321,9 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_object *old_exception = NULL; const zend_op *old_opline_before_exception = NULL; if (EG(exception)) { - if (EG(current_execute_data)) { + if (EG(current_execute_data) + && EG(current_execute_data)->opline + && EG(current_execute_data)->opline->opcode == ZEND_HANDLE_EXCEPTION) { EG(current_execute_data)->opline = EG(opline_before_exception); old_opline_before_exception = EG(opline_before_exception); } @@ -337,7 +339,7 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ zend_generator_resume(generator); if (old_exception) { - if (EG(current_execute_data)) { + if (old_opline_before_exception) { EG(current_execute_data)->opline = EG(exception_op); EG(opline_before_exception) = old_opline_before_exception; }