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     static SignalDefs signalDefs[] = {
98         { SIGINT,  "SIGINT - Terminal interrupt signal" },
99         { SIGILL,  "SIGILL - Illegal instruction signal" },
100         { SIGFPE,  "SIGFPE - Floating point error signal" },
101         { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
102         { SIGTERM, "SIGTERM - Termination request signal" },
103         { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
104     };
105 
106 
handleSignal(int sig)107     void FatalConditionHandler::handleSignal( int sig ) {
108         char const * name = "<unknown signal>";
109         for (auto const& def : signalDefs) {
110             if (sig == def.id) {
111                 name = def.name;
112                 break;
113             }
114         }
115         reset();
116         reportFatal(name);
117         raise( sig );
118     }
119 
FatalConditionHandler()120     FatalConditionHandler::FatalConditionHandler() {
121         isSet = true;
122         stack_t sigStack;
123         sigStack.ss_sp = altStackMem;
124         sigStack.ss_size = SIGSTKSZ;
125         sigStack.ss_flags = 0;
126         sigaltstack(&sigStack, &oldSigStack);
127         struct sigaction sa = { };
128 
129         sa.sa_handler = handleSignal;
130         sa.sa_flags = SA_ONSTACK;
131         for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
132             sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
133         }
134     }
135 
136 
~FatalConditionHandler()137     FatalConditionHandler::~FatalConditionHandler() {
138         reset();
139     }
140 
reset()141     void FatalConditionHandler::reset() {
142         if( isSet ) {
143             // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
144             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
145                 sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
146             }
147             // Return the old stack
148             sigaltstack(&oldSigStack, nullptr);
149             isSet = false;
150         }
151     }
152 
153     bool FatalConditionHandler::isSet = false;
154     struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
155     stack_t FatalConditionHandler::oldSigStack = {};
156     char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
157 
158 
159 } // namespace Catch
160 
161 #else
162 
163 namespace Catch {
reset()164     void FatalConditionHandler::reset() {}
165 }
166 
167 #endif // signals/SEH handling
168 
169 #if defined(__GNUC__)
170 #    pragma GCC diagnostic pop
171 #endif
172