From 75620c69c6a42cc605cffb5cdc7e048375ae7138 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Thu, 19 Sep 2024 01:30:37 +0200 Subject: [PATCH 1/2] Fix printing backtrace during generator closing Fixes GH-15851 --- Zend/tests/generators/gh15851.phpt | 27 +++++++++++++++++++++++++++ Zend/zend_builtin_functions.c | 10 ++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Zend/tests/generators/gh15851.phpt diff --git a/Zend/tests/generators/gh15851.phpt b/Zend/tests/generators/gh15851.phpt new file mode 100644 index 000000000000..8a7fa6294e25 --- /dev/null +++ b/Zend/tests/generators/gh15851.phpt @@ -0,0 +1,27 @@ +--TEST-- +GH-15851: Access on NULL when printing backtrace with freed generator +--FILE-- + +--EXPECTF-- +#0 %s(%d): Foo->__destruct() +#1 %s(%d): bar() diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 72cb07decbd3..898318102c1d 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -1725,6 +1725,16 @@ ZEND_API void zend_fetch_debug_backtrace(zval *return_value, int skip_last, int } while (call && (limit == 0 || frameno < limit)) { + if (UNEXPECTED(!call->func)) { + /* This is the fake frame inserted for nested generators. Normally, + * this frame is preceded by the actual generator frame and then + * replaced by zend_generator_check_placeholder_frame() below. + * However, the frame is popped before cleaning the stack frame, + * which is observable by destructors. */ + call = zend_generator_check_placeholder_frame(call); + ZEND_ASSERT(call->func); + } + zend_execute_data *prev = call->prev_execute_data; if (!prev) { From 9cf34d95717c29c92f8a00a270b192eb7a0e0481 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Fri, 27 Sep 2024 12:15:16 +0200 Subject: [PATCH 2/2] Fix cli test Set the func field for the empty top frame in cli to distinguish it from the fake generator frame. --- sapi/cli/php_cli.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sapi/cli/php_cli.c b/sapi/cli/php_cli.c index ff525438ebc0..34046466ae7e 100644 --- a/sapi/cli/php_cli.c +++ b/sapi/cli/php_cli.c @@ -1075,6 +1075,7 @@ static int do_cli(int argc, char **argv) /* {{{ */ object_init_ex(&ref, pce); memset(&execute_data, 0, sizeof(zend_execute_data)); + execute_data.func = (zend_function *) &zend_pass_function; EG(current_execute_data) = &execute_data; zend_call_known_instance_method_with_1_params( pce->constructor, Z_OBJ(ref), NULL, &arg);