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 #ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
10 #define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
11 
12 
13 namespace Catch {
14 
15     // Report the error condition
reportFatal(std::string const & message)16     inline void reportFatal( std::string const& message ) {
17         IContext& context = Catch::getCurrentContext();
18         IResultCapture* resultCapture = context.getResultCapture();
19         resultCapture->handleFatalErrorCondition( message );
20     }
21 
22 } // namespace Catch
23 
24 #if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
25 #include "catch_windows_h_proxy.h"
26 
27 #  if !defined ( CATCH_CONFIG_WINDOWS_SEH )
28 
29 namespace Catch {
30     struct FatalConditionHandler {
resetCatch::FatalConditionHandler31         void reset() {}
32     };
33 }
34 
35 #  else // CATCH_CONFIG_WINDOWS_SEH is defined
36 
37 namespace Catch {
38 
39     struct SignalDefs { DWORD id; const char* name; };
40     extern SignalDefs signalDefs[];
41     // There is no 1-1 mapping between signals and windows exceptions.
42     // Windows can easily distinguish between SO and SigSegV,
43     // but SigInt, SigTerm, etc are handled differently.
44     SignalDefs signalDefs[] = {
45         { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
46         { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
47         { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
48         { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
49     };
50 
51     struct FatalConditionHandler {
52 
handleVectoredExceptionCatch::FatalConditionHandler53         static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
54             for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
55                 if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
56                     reportFatal(signalDefs[i].name);
57                 }
58             }
59             // If its not an exception we care about, pass it along.
60             // This stops us from eating debugger breaks etc.
61             return EXCEPTION_CONTINUE_SEARCH;
62         }
63 
FatalConditionHandlerCatch::FatalConditionHandler64         FatalConditionHandler() {
65             isSet = true;
66             // 32k seems enough for Catch to handle stack overflow,
67             // but the value was found experimentally, so there is no strong guarantee
68             guaranteeSize = 32 * 1024;
69             exceptionHandlerHandle = CATCH_NULL;
70             // Register as first handler in current chain
71             exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
72             // Pass in guarantee size to be filled
73             SetThreadStackGuarantee(&guaranteeSize);
74         }
75 
resetCatch::FatalConditionHandler76         static void reset() {
77             if (isSet) {
78                 // Unregister handler and restore the old guarantee
79                 RemoveVectoredExceptionHandler(exceptionHandlerHandle);
80                 SetThreadStackGuarantee(&guaranteeSize);
81                 exceptionHandlerHandle = CATCH_NULL;
82                 isSet = false;
83             }
84         }
85 
~FatalConditionHandlerCatch::FatalConditionHandler86         ~FatalConditionHandler() {
87             reset();
88         }
89     private:
90         static bool isSet;
91         static ULONG guaranteeSize;
92         static PVOID exceptionHandlerHandle;
93     };
94 
95     bool FatalConditionHandler::isSet = false;
96     ULONG FatalConditionHandler::guaranteeSize = 0;
97     PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
98 
99 } // namespace Catch
100 
101 #  endif // CATCH_CONFIG_WINDOWS_SEH
102 
103 #else // Not Windows - assumed to be POSIX compatible //////////////////////////
104 
105 #  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
106 
107 namespace Catch {
108     struct FatalConditionHandler {
resetCatch::FatalConditionHandler109         void reset() {}
110     };
111 }
112 
113 
114 #  else // CATCH_CONFIG_POSIX_SIGNALS is defined
115 
116 #include <signal.h>
117 
118 namespace Catch {
119 
120     struct SignalDefs {
121         int id;
122         const char* name;
123     };
124     extern SignalDefs signalDefs[];
125     SignalDefs signalDefs[] = {
126             { SIGINT,  "SIGINT - Terminal interrupt signal" },
127             { SIGILL,  "SIGILL - Illegal instruction signal" },
128             { SIGFPE,  "SIGFPE - Floating point error signal" },
129             { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
130             { SIGTERM, "SIGTERM - Termination request signal" },
131             { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
132     };
133 
134     struct FatalConditionHandler {
135 
136         static bool isSet;
137         static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
138         static stack_t oldSigStack;
139         static char altStackMem[SIGSTKSZ];
140 
handleSignalCatch::FatalConditionHandler141         static void handleSignal( int sig ) {
142             std::string name = "<unknown signal>";
143             for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
144                 SignalDefs &def = signalDefs[i];
145                 if (sig == def.id) {
146                     name = def.name;
147                     break;
148                 }
149             }
150             reset();
151             reportFatal(name);
152             raise( sig );
153         }
154 
FatalConditionHandlerCatch::FatalConditionHandler155         FatalConditionHandler() {
156             isSet = true;
157             stack_t sigStack;
158             sigStack.ss_sp = altStackMem;
159             sigStack.ss_size = SIGSTKSZ;
160             sigStack.ss_flags = 0;
161             sigaltstack(&sigStack, &oldSigStack);
162             struct sigaction sa = { 0 };
163 
164             sa.sa_handler = handleSignal;
165             sa.sa_flags = SA_ONSTACK;
166             for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
167                 sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
168             }
169         }
170 
171 
~FatalConditionHandlerCatch::FatalConditionHandler172         ~FatalConditionHandler() {
173             reset();
174         }
resetCatch::FatalConditionHandler175         static void reset() {
176             if( isSet ) {
177                 // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
178                 for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
179                     sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
180                 }
181                 // Return the old stack
182                 sigaltstack(&oldSigStack, CATCH_NULL);
183                 isSet = false;
184             }
185         }
186     };
187 
188     bool FatalConditionHandler::isSet = false;
189     struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
190     stack_t FatalConditionHandler::oldSigStack = {};
191     char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
192 
193 
194 } // namespace Catch
195 
196 #  endif // CATCH_CONFIG_POSIX_SIGNALS
197 
198 #endif // not Windows
199 
200 #endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
201