From 9431ad33a531bee1b96ab203d9067e03198900c8 Mon Sep 17 00:00:00 2001 From: Pratik Bhujel Date: Sat, 7 Nov 2026 16:50:24 +0545 Subject: [PATCH] Fix GH-24430: Check for user frame before reading opline in generator dtor When zend_generator_dtor_storage() runs finally blocks while an exception is already pending (such as when an earlier generator's finally block threw during gc_collect_cycles()), EG(current_execute_data) can be an internal function frame like gc_destructor_fiber() whose opline slot on the VM stack is uninitialized. Check that EG(current_execute_data)->func is user code before dereferencing EG(current_execute_data)->opline->opcode, matching is_handle_exception_set(). Fixes GH-24430 --- Zend/tests/generators/gh24430.phpt | 51 ++++++++++++++++++++++++++++++ Zend/zend_generators.c | 3 +- 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/generators/gh24430.phpt diff --git a/Zend/tests/generators/gh24430.phpt b/Zend/tests/generators/gh24430.phpt new file mode 100644 index 000000000000..cd1cc54c000d --- /dev/null +++ b/Zend/tests/generators/gh24430.phpt @@ -0,0 +1,51 @@ +--TEST-- +GH-24430 (SEGV in zend_generator_dtor_storage when GC closes cyclic generators in a Fiber) +--FILE-- +current(); + $holder = new stdClass(); + $holder->generator = $generator; + $holder->self = $holder; + $holders[] = $holder; + } + unset($holders); + try { + gc_collect_cycles(); + } catch (RuntimeException $e) { + echo $e->getMessage(), "\n"; + } +}))->start(); + +echo "Done\n"; +?> +--EXPECT-- +from finally +Done diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index ef645f8f9a3a..305b1169c919 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -314,7 +314,8 @@ static void zend_generator_dtor_storage(zend_object *object) /* {{{ */ const zend_op *old_opline_before_exception = NULL; if (EG(exception)) { if (EG(current_execute_data) - && EG(current_execute_data)->opline + && EG(current_execute_data)->func + && ZEND_USER_CODE(EG(current_execute_data)->func->common.type) && 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);