Currently, when std::terminate() is called with a foreign exception active, since nothing in the path checks whether the exception matches the `GNUCC++\0` personality, a foreign exception can go into the verbose terminate handler, and get treated as though it were a C++ exception.
Reflection is attempted, and boom. UB. This patch should eliminate that UB. Signed-off-by: Julia DeMille <m...@jdemille.com> --- libstdc++-v3/ChangeLog | 9 +++++++++ libstdc++-v3/libsupc++/eh_type.cc | 11 +++++++++++ libstdc++-v3/libsupc++/vterminate.cc | 25 ++++++++++++++++++++----- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 36257cc4427..bfef0ed8ef1 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,12 @@ +2024-01-13 Julia DeMille <m...@jdemille.com> + * libsupc++/eh_type.cc (__cxa_current_exception_type): + Return NULL if the current exception is not the `GNUCC++\0` + personality. + * libsupc++/vterminate.cc: + Check for both exception header and exception type. If there + is an exception header, but no exception type, state in termination + message that a foreign exception was active. + 2024-01-13 Jonathan Wakely <jwak...@redhat.com> PR libstdc++/107466 diff --git a/libstdc++-v3/libsupc++/eh_type.cc b/libstdc++-v3/libsupc++/eh_type.cc index 03c677b7e13..e0824eab4d4 100644 --- a/libstdc++-v3/libsupc++/eh_type.cc +++ b/libstdc++-v3/libsupc++/eh_type.cc @@ -36,9 +36,20 @@ extern "C" std::type_info *__cxa_current_exception_type () _GLIBCXX_NOTHROW { __cxa_eh_globals *globals = __cxa_get_globals (); + + if (!globals) + return 0; + __cxa_exception *header = globals->caughtExceptions; + if (header) { + // It is UB to try and inspect an exception that isn't ours. + // This extends to attempting to perform run-time reflection, as the ABI + // is unknown. + if (!__is_gxx_exception_class (header->unwindHeader.exception_class)) + return 0; + if (__is_dependent_exception (header->unwindHeader.exception_class)) { __cxa_dependent_exception *de = diff --git a/libstdc++-v3/libsupc++/vterminate.cc b/libstdc++-v3/libsupc++/vterminate.cc index 23deeef5289..f931d951526 100644 --- a/libstdc++-v3/libsupc++/vterminate.cc +++ b/libstdc++-v3/libsupc++/vterminate.cc @@ -25,11 +25,12 @@ #include <bits/c++config.h> #if _GLIBCXX_HOSTED -#include <cstdlib> -#include <exception> +#include "unwind-cxx.h" #include <bits/exception_defines.h> +#include <cstdio> +#include <cstdlib> #include <cxxabi.h> -# include <cstdio> +#include <exception> using namespace std; using namespace abi; @@ -51,10 +52,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } terminating = true; + __cxa_eh_globals *globals = __cxa_get_globals (); + if (!globals) + { + fputs ("terminate called", stderr); + abort (); + } + // Make sure there was an exception; terminate is also called for an // attempt to rethrow when there is no suitable exception. - type_info *t = __cxa_current_exception_type(); - if (t) + type_info *t = __cxa_current_exception_type (); + __cxa_exception *header = globals->caughtExceptions; + + if (t && header) { // Note that "name" is the mangled name. char const *name = t->name(); @@ -89,6 +99,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif __catch(...) { } } + else if (header) + { + fputs ("terminate called after a foreign exception was thrown\n", + stderr); + } else fputs("terminate called without an active exception\n", stderr); -- 2.43.0