diff --git a/system/lib/libcxx/include/exception b/system/lib/libcxx/include/exception index 816f259f878b2..5f812d72e6ee5 100644 --- a/system/lib/libcxx/include/exception +++ b/system/lib/libcxx/include/exception @@ -45,7 +45,13 @@ unexpected_handler get_unexpected() noexcept; typedef void (*terminate_handler)(); terminate_handler set_terminate(terminate_handler f ) noexcept; terminate_handler get_terminate() noexcept; +#ifdef __USING_WASM_EXCEPTIONS__ +// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup +// // from which std::__terminate is called again, causing an infinite loop +[[noreturn]] void terminate(); +#else [[noreturn]] void terminate() noexcept; +#endif bool uncaught_exception() noexcept; int uncaught_exceptions() noexcept; // C++17 @@ -128,7 +134,13 @@ _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void unexpected(); typedef void (*terminate_handler)(); _LIBCPP_FUNC_VIS terminate_handler set_terminate(terminate_handler) _NOEXCEPT; _LIBCPP_FUNC_VIS terminate_handler get_terminate() _NOEXCEPT; +#ifdef __USING_WASM_EXCEPTIONS__ +// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup +// // from which std::__terminate is called again, causing an infinite loop +_LIBCPP_NORETURN _LIBCPP_FUNC_VIS void terminate(); +#else _LIBCPP_NORETURN _LIBCPP_FUNC_VIS void terminate() _NOEXCEPT; +#endif _LIBCPP_FUNC_VIS bool uncaught_exception() _NOEXCEPT; _LIBCPP_FUNC_VIS _LIBCPP_AVAILABILITY_UNCAUGHT_EXCEPTIONS int uncaught_exceptions() _NOEXCEPT; diff --git a/system/lib/libcxxabi/src/cxa_handlers.cpp b/system/lib/libcxxabi/src/cxa_handlers.cpp index 340446da46468..a6606b68dd07c 100644 --- a/system/lib/libcxxabi/src/cxa_handlers.cpp +++ b/system/lib/libcxxabi/src/cxa_handlers.cpp @@ -50,7 +50,13 @@ get_terminate() noexcept } void +#ifdef __USING_WASM_EXCEPTIONS__ +// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup +// from which std::__terminate is called again, causing an infinite loop +__terminate(terminate_handler func) +#else __terminate(terminate_handler func) noexcept +#endif { #ifndef _LIBCXXABI_NO_EXCEPTIONS try @@ -71,7 +77,13 @@ __terminate(terminate_handler func) noexcept __attribute__((noreturn)) void +#ifdef __USING_WASM_EXCEPTIONS__ +// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup +// from which std::terminate is called again, causing an infinite loop +terminate() +#else terminate() noexcept +#endif { #if !defined(_LIBCXXABI_NO_EXCEPTIONS) && !defined(__USING_EMSCRIPTEN_EXCEPTIONS__) // If there might be an uncaught exception diff --git a/system/lib/libcxxabi/src/cxa_handlers.h b/system/lib/libcxxabi/src/cxa_handlers.h index da113b82db34e..316e253d23048 100644 --- a/system/lib/libcxxabi/src/cxa_handlers.h +++ b/system/lib/libcxxabi/src/cxa_handlers.h @@ -25,7 +25,13 @@ __unexpected(unexpected_handler func); _LIBCXXABI_HIDDEN _LIBCXXABI_NORETURN void +#ifdef __USING_WASM_EXCEPTIONS__ +// In Wasm EH, a JS exception thrown by abort() is caught by 'noexcept' cleanup +// // from which std::__terminate is called again, causing an infinite loop +__terminate(terminate_handler func); +#else __terminate(terminate_handler func) noexcept; +#endif } // std diff --git a/tests/test_core.py b/tests/test_core.py index 2a03fac6f32d7..bcd80b840a6fa 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1639,6 +1639,24 @@ class Polymorphic {virtual void member(){}}; } ''', 'exception caught: std::bad_typeid') + @with_both_eh_sjlj + def test_std_terminate(self): + # std::terminate eventually calls abort(), which is implemented with + # throwing a JS exception, which used to cause an infinite loop that + # exhausted the call stack. The reason is std::terminate is marked + # 'noexcept' in the upstream LLVM, which generates cleanuppads that call + # std::terminate in case of an unexpected second exception happens while + # aborting, and our abort() was considered as that second exception. We + # removed 'noexcept' from std::terminate signature when Wasm EH is enabled + # to avoid this issue. + err = self.do_run(r''' +#include +int main() { + std::terminate(); +} +''', assert_returncode=NON_ZERO) + self.assertNotContained('Maximum call stack size exceeded', err) + def test_iostream_ctors(self): # iostream stuff must be globally constructed before user global # constructors, so iostream works in global constructors