1 /* 2 * Created by Phil on 21/08/2014 3 * Copyright 2014 Two Blue Cubes Ltd. All rights reserved. 4 * 5 * Distributed under the Boost Software License, Version 1.0. (See accompanying 6 * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 * 8 */ 9 10 #include "catch_fatal_condition.h" 11 12 #include "catch_context.h" 13 #include "catch_interfaces_capture.h" 14 15 #if defined(__GNUC__) 16 # pragma GCC diagnostic push 17 # pragma GCC diagnostic ignored "-Wmissing-field-initializers" 18 #endif 19 20 #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) 21 22 namespace { 23 // Report the error condition reportFatal(char const * const message)24 void reportFatal( char const * const message ) { 25 Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); 26 } 27 } 28 29 #endif // signals/SEH handling 30 31 #if defined( CATCH_CONFIG_WINDOWS_SEH ) 32 33 namespace Catch { 34 struct SignalDefs { DWORD id; const char* name; }; 35 36 // There is no 1-1 mapping between signals and windows exceptions. 37 // Windows can easily distinguish between SO and SigSegV, 38 // but SigInt, SigTerm, etc are handled differently. 39 static SignalDefs signalDefs[] = { 40 { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, 41 { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, 42 { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, 43 { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, 44 }; 45 handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo)46 LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { 47 for (auto const& def : signalDefs) { 48 if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { 49 reportFatal(def.name); 50 } 51 } 52 // If its not an exception we care about, pass it along. 53 // This stops us from eating debugger breaks etc. 54 return EXCEPTION_CONTINUE_SEARCH; 55 } 56 FatalConditionHandler()57 FatalConditionHandler::FatalConditionHandler() { 58 isSet = true; 59 // 32k seems enough for Catch to handle stack overflow, 60 // but the value was found experimentally, so there is no strong guarantee 61 guaranteeSize = 32 * 1024; 62 exceptionHandlerHandle = nullptr; 63 // Register as first handler in current chain 64 exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); 65 // Pass in guarantee size to be filled 66 SetThreadStackGuarantee(&guaranteeSize); 67 } 68 reset()69 void FatalConditionHandler::reset() { 70 if (isSet) { 71 RemoveVectoredExceptionHandler(exceptionHandlerHandle); 72 SetThreadStackGuarantee(&guaranteeSize); 73 exceptionHandlerHandle = nullptr; 74 isSet = false; 75 } 76 } 77 ~FatalConditionHandler()78 FatalConditionHandler::~FatalConditionHandler() { 79 reset(); 80 } 81 82 bool FatalConditionHandler::isSet = false; 83 ULONG FatalConditionHandler::guaranteeSize = 0; 84 PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; 85 86 87 } // namespace Catch 88 89 #elif defined( CATCH_CONFIG_POSIX_SIGNALS ) 90 91 namespace Catch { 92 93 struct SignalDefs { 94 int id; 95 const char* name; 96 }; 97 98 // 32kb for the alternate stack seems to be sufficient. However, this value 99 // is experimentally determined, so that's not guaranteed. 100 constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; 101 102 static SignalDefs signalDefs[] = { 103 { SIGINT, "SIGINT - Terminal interrupt signal" }, 104 { SIGILL, "SIGILL - Illegal instruction signal" }, 105 { SIGFPE, "SIGFPE - Floating point error signal" }, 106 { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, 107 { SIGTERM, "SIGTERM - Termination request signal" }, 108 { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } 109 }; 110 111 handleSignal(int sig)112 void FatalConditionHandler::handleSignal( int sig ) { 113 char const * name = "<unknown signal>"; 114 for (auto const& def : signalDefs) { 115 if (sig == def.id) { 116 name = def.name; 117 break; 118 } 119 } 120 reset(); 121 reportFatal(name); 122 raise( sig ); 123 } 124 FatalConditionHandler()125 FatalConditionHandler::FatalConditionHandler() { 126 isSet = true; 127 stack_t sigStack; 128 sigStack.ss_sp = altStackMem; 129 sigStack.ss_size = sigStackSize; 130 sigStack.ss_flags = 0; 131 sigaltstack(&sigStack, &oldSigStack); 132 struct sigaction sa = { }; 133 134 sa.sa_handler = handleSignal; 135 sa.sa_flags = SA_ONSTACK; 136 for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { 137 sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); 138 } 139 } 140 141 ~FatalConditionHandler()142 FatalConditionHandler::~FatalConditionHandler() { 143 reset(); 144 } 145 reset()146 void FatalConditionHandler::reset() { 147 if( isSet ) { 148 // Set signals back to previous values -- hopefully nobody overwrote them in the meantime 149 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { 150 sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); 151 } 152 // Return the old stack 153 sigaltstack(&oldSigStack, nullptr); 154 isSet = false; 155 } 156 } 157 158 bool FatalConditionHandler::isSet = false; 159 struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; 160 stack_t FatalConditionHandler::oldSigStack = {}; 161 char FatalConditionHandler::altStackMem[sigStackSize] = {}; 162 163 164 } // namespace Catch 165 166 #else 167 168 namespace Catch { reset()169 void FatalConditionHandler::reset() {} 170 } 171 172 #endif // signals/SEH handling 173 174 #if defined(__GNUC__) 175 # pragma GCC diagnostic pop 176 #endif 177