From cc567486465aeca5f2dcc58400814b64414f4fce Mon Sep 17 00:00:00 2001 From: AlxcBTW <187763517+AlxcBTW@users.noreply.github.com> Date: Sat, 10 Oct 2026 07:16:59 +0200 Subject: [PATCH] fix(libc): release future results' exceptions through libc on Windows libc implements the C++ exception runtime and already defines exception_ptr's out-of-line members, so host modules that link libc reference-count libc's exceptions. Code inside libstdc++-6.dll does not see those definitions on Windows: std::__future_base::_Result_base's destructor is one of them, and it releases the stored exception with libstdc++'s own exception_ptr::_M_release. That decrements a count 0xa0 bytes before the object, while libc keeps it 0x80 bytes before, so the decrement lands in the heap memory in front of libc's allocation and the exception is never freed. VideoOutDriver reports the presentation thread's start-up failure through a std::promise, so video_out_flip_lifetime sometimes ended with 0xc0000374 when SDL's offscreen driver refused the window. libc now defines the base destructor, with a _nid_postfix twin so that the NID-patched libc keeps its name, and modules bind it there. --- .../libc/src/exception/ExceptionPointer.cpp | 11 ++++++++++ core/libs/tests/WindowsExceptions.cpp | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/core/libs/prx/libc/src/exception/ExceptionPointer.cpp b/core/libs/prx/libc/src/exception/ExceptionPointer.cpp index eba668749..447d85ac4 100644 --- a/core/libs/prx/libc/src/exception/ExceptionPointer.cpp +++ b/core/libs/prx/libc/src/exception/ExceptionPointer.cpp @@ -1,12 +1,23 @@ #include #ifndef _LIBCPP_VERSION +#include + extern "C" { void ExceptionPointerAddref(std::exception_ptr* self) noexcept asm("_ZNSt15__exception_ptr13exception_ptr9_M_addrefEv"); void ExceptionPointerRelease(std::exception_ptr* self) noexcept asm("_ZNSt15__exception_ptr13exception_ptr10_M_releaseEv"); void* ExceptionPointerGet(const std::exception_ptr* self) noexcept asm("_ZNKSt15__exception_ptr13exception_ptr6_M_getEv"); void ExceptionPointerConstruct(std::exception_ptr* self, void* exception) noexcept asm("_ZNSt15__exception_ptr13exception_ptrC1EPv"); const std::type_info* ExceptionPointerType(const std::exception_ptr* self) noexcept asm("_ZNKSt15__exception_ptr13exception_ptr20__cxa_exception_typeEv"); +void FutureResultDestroy(std::__future_base::_Result_base* self) noexcept asm("_ZNSt13__future_base12_Result_baseD2Ev"); + +void FutureResultDestroy(std::__future_base::_Result_base* self) noexcept { + self->_M_error.~exception_ptr(); +} + +void APS5_VABI _ZNSt13__future_base12_Result_baseD2Ev_nid_postfix(std::__future_base::_Result_base* self) noexcept { + FutureResultDestroy(self); +} void APS5_VABI _ZNSt15__exception_ptr13exception_ptr9_M_addrefEv_nid_postfix(std::exception_ptr* self) noexcept { ExceptionPointerAddref(self); diff --git a/core/libs/tests/WindowsExceptions.cpp b/core/libs/tests/WindowsExceptions.cpp index 2e13adb43..deb55e6dd 100644 --- a/core/libs/tests/WindowsExceptions.cpp +++ b/core/libs/tests/WindowsExceptions.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -105,9 +106,30 @@ static void testExceptionPointer() { if (std::current_exception()) throw std::runtime_error("stale current exception"); } +static void testFutureException() { + std::atomic count{0}; + { + std::promise promise; + auto future = promise.get_future(); + try { + throw TrackedError(&count); + } catch (const TrackedError&) { + promise.set_exception(std::current_exception()); + } + try { + future.get(); + throw std::runtime_error("future did not rethrow"); + } catch (const TrackedError& error) { + if (std::strcmp(error.what(), "retained error")) throw std::runtime_error("future rethrew another error"); + } + } + if (count != 1) throw std::runtime_error("future result destroyed its exception an incorrect number of times"); +} + int main() { TestTypeInfoVtables(); testExceptionPointer(); + testFutureException(); try { Rethrow(); return 1;