From 2ce64d1d8f265bba48f3b7bb2130d0cfbfb51ed4 Mon Sep 17 00:00:00 2001 From: "Kochetkov, Yuriy" Date: Tue, 8 Feb 2022 13:43:40 +0300 Subject: [PATCH] Fix disengage failure logs for FatalConditionHandlerGuard FatalConditionHandlerGuard is used within RunContext::invokeActiveTestCase(). The intent of this guard is to avoid binary crash without failed test being reported. Still in case FatalConditionHandlerGuard destructor being called during stack unwinding AND finds unexpected top-level filter for SEH unhandled exception, the binary may still crash. As result of such crash the original exception details are being hidden. As the Catch2 provides only `CATCH_CATCH_ANON` macro, with no access to exception details by design, looks like the best way to handle issue is to: - state requirements explicitly by `noexcept` specifier - use `Catch::cerr()` to print out possible issue notification Signed-off-by: Kochetkov, Yuriy --- .../internal/catch_fatal_condition_handler.cpp | 12 +++++++----- .../internal/catch_fatal_condition_handler.hpp | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/catch2/internal/catch_fatal_condition_handler.cpp b/src/catch2/internal/catch_fatal_condition_handler.cpp index 60cecd7a..410b9b8f 100644 --- a/src/catch2/internal/catch_fatal_condition_handler.cpp +++ b/src/catch2/internal/catch_fatal_condition_handler.cpp @@ -40,7 +40,7 @@ namespace Catch { // If neither SEH nor signal handling is required, the handler impls // do not have to do anything, and can be empty. void FatalConditionHandler::engage_platform() {} - void FatalConditionHandler::disengage_platform() {} + void FatalConditionHandler::disengage_platform() noexcept {} FatalConditionHandler::FatalConditionHandler() = default; FatalConditionHandler::~FatalConditionHandler() = default; @@ -124,9 +124,11 @@ namespace Catch { previousTopLevelExceptionFilter = SetUnhandledExceptionFilter(topLevelExceptionFilter); } - void FatalConditionHandler::disengage_platform() { + void FatalConditionHandler::disengage_platform() noexcept { if (SetUnhandledExceptionFilter(previousTopLevelExceptionFilter) != topLevelExceptionFilter) { - CATCH_RUNTIME_ERROR("Could not restore previous top level exception filter"); + Catch::cerr() + << "Unexpected SEH unhandled exception filter on disengage." + << " The filter was restored, but might be rolled back unexpectedly."; } previousTopLevelExceptionFilter = nullptr; } @@ -168,7 +170,7 @@ namespace Catch { static stack_t oldSigStack{}; static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; - static void restorePreviousSignalHandlers() { + static void restorePreviousSignalHandlers() noexcept { // We set signal handlers back to the previous ones. Hopefully // nobody overwrote them in the meantime, and doesn't expect // their signal handlers to live past ours given that they @@ -231,7 +233,7 @@ namespace Catch { #endif - void FatalConditionHandler::disengage_platform() { + void FatalConditionHandler::disengage_platform() noexcept { restorePreviousSignalHandlers(); } diff --git a/src/catch2/internal/catch_fatal_condition_handler.hpp b/src/catch2/internal/catch_fatal_condition_handler.hpp index 4a2818cb..389dab5d 100644 --- a/src/catch2/internal/catch_fatal_condition_handler.hpp +++ b/src/catch2/internal/catch_fatal_condition_handler.hpp @@ -32,7 +32,7 @@ namespace Catch { // Should be if-defed to work on current platform, can assume // engage-disengage 1:1 pairing. void engage_platform(); - void disengage_platform(); + void disengage_platform() noexcept; public: // Should also have platform-specific implementations as needed FatalConditionHandler(); @@ -44,7 +44,7 @@ namespace Catch { engage_platform(); } - void disengage() { + void disengage() noexcept { assert(m_started && "Handler cannot be uninstalled without being installed first"); m_started = false; disengage_platform();