From be7f3aa474c1328234a0fb39b56ded6609e9dca1 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Sat, 1 Jun 2024 02:38:55 +0200 Subject: [PATCH] Fix GH-14387: Crash when stack walking in destructor of yielded from values during Generator->throw() --- NEWS | 2 ++ Zend/tests/generators/gh14387.phpt | 35 ++++++++++++++++++++++++++++++ Zend/zend_generators.c | 1 + 3 files changed, 38 insertions(+) create mode 100644 Zend/tests/generators/gh14387.phpt diff --git a/NEWS b/NEWS index d9a21aba3f429..9711904b853ac 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,8 @@ PHP NEWS . Fixed bug GH-14315 (Incompatible pointer type warnings). (Peter Kokot) . Fixed bug GH-12814 (max_execution_time reached too early on MacOS 14 when running on Apple Silicon). (Manuel Kress) + . Fixed bug GH-14387 (Crash when stack walking in destructor of yielded from + values during Generator->throw()). (Bob) - BCMatch: . Fixed bug (bcpowmod() with mod = -1 returns 1 when it must be 0). (Girgias) diff --git a/Zend/tests/generators/gh14387.phpt b/Zend/tests/generators/gh14387.phpt new file mode 100644 index 0000000000000..dab0ba7c94a9e --- /dev/null +++ b/Zend/tests/generators/gh14387.phpt @@ -0,0 +1,35 @@ +--TEST-- +GH-14387 (Crash when stack walking in destructor of yielded from values during Generator->throw()) +--FILE-- +valid(); +} + +$g = (function () { + yield from [null, new class { + function __destruct() { + // Trigger a stack walk, hitting a bad frame. + throw new Exception; + } + }]; +})(); + +prime($g); + +$g->throw(new Error); + +?> +--EXPECTF-- +Fatal error: Uncaught Error in %s:%d +Stack trace: +#0 {main} + +Next Exception in %s:%d +Stack trace: +#0 %s(%d): class@anonymous->__destruct() +#1 [internal function]: {%s}() +#2 %s(%d): Generator->throw(Object(Error)) +#3 {main} + thrown in %s on line %d diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c index b569374185952..1d785377e82e7 100644 --- a/Zend/zend_generators.c +++ b/Zend/zend_generators.c @@ -462,6 +462,7 @@ static void zend_generator_throw_exception(zend_generator *generator, zval *exce * to pretend the exception happened during the YIELD opcode. */ EG(current_execute_data) = generator->execute_data; generator->execute_data->opline--; + generator->execute_data->prev_execute_data = original_execute_data; if (exception) { zend_throw_exception_object(exception);