From b145094423c7eae32d8d1357679ae4b849b8df3c Mon Sep 17 00:00:00 2001 From: Pratik Bhujel Date: Fri, 6 Nov 2026 13:30:10 +0545 Subject: [PATCH] Fix GH-11821: Allow graceful exit during class fetch without uncaught error When an autoloader or signal handler calls exit() during a class fetch where exceptions cannot be caught (such as trait binding), report_class_fetch_error() previously passed the in-flight UnwindExit to zend_exception_uncaught_error(). Because UnwindExit does not have string conversion semantics, this triggered an "Object of class UnwindExit could not be converted to string" error and overrode the exit status with a fatal error. Instead, let report_class_fetch_error() return early when an unwind or graceful exit is in flight so the VM opcode loop can gracefully unwind the stack, invoke destructors, and exit with the expected status. Also guard against the preloading sentinel ((void *)(uintptr_t)-1) where error reporting is disabled. Closes GH-11821. --- Zend/tests/gh11821.phpt | 16 ++++++++++++++++ Zend/zend_execute_API.c | 6 ++++++ 2 files changed, 22 insertions(+) create mode 100644 Zend/tests/gh11821.phpt diff --git a/Zend/tests/gh11821.phpt b/Zend/tests/gh11821.phpt new file mode 100644 index 000000000000..4e34dbe2b4d5 --- /dev/null +++ b/Zend/tests/gh11821.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-11821: Exit in autoloader during class fetch should not cause UnwindExit string conversion error +--FILE-- + +--EXPECT-- diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 30eb6ec8af3d..b1a7f6befc5e 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1719,6 +1719,12 @@ static ZEND_COLD void report_class_fetch_error(zend_string *class_name, uint32_t } if (EG(exception)) { + if (EG(exception) == (void *)(uintptr_t)-1) { + return; + } + if (zend_is_unwind_exit(EG(exception)) || zend_is_graceful_exit(EG(exception))) { + return; + } if (!(fetch_type & ZEND_FETCH_CLASS_EXCEPTION)) { zend_exception_uncaught_error("During class fetch"); }