| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  *  Created by Phil on 21/08/2014 | 
					
						
							|  |  |  |  *  Copyright 2014 Two Blue Cubes Ltd. All rights reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  Distributed under the Boost Software License, Version 1.0. (See accompanying | 
					
						
							|  |  |  |  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "catch_fatal_condition.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "catch_context.h"
 | 
					
						
							|  |  |  | #include "catch_interfaces_capture.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-21 15:23:30 +01:00
										 |  |  | #if defined(__GNUC__)
 | 
					
						
							|  |  |  | #    pragma GCC diagnostic push
 | 
					
						
							|  |  |  | #    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 16:17:26 +01:00
										 |  |  | #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  | namespace { | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |     // Report the error condition
 | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  |     void reportFatal( char const * const message ) { | 
					
						
							| 
									
										
										
										
											2017-11-21 11:08:08 +00:00
										 |  |  |         Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-28 16:17:26 +01:00
										 |  |  | #endif // signals/SEH handling
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 14:56:07 +01:00
										 |  |  | #if defined( CATCH_CONFIG_WINDOWS_SEH )
 | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							|  |  |  |     struct SignalDefs { DWORD id; const char* name; }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // There is no 1-1 mapping between signals and windows exceptions.
 | 
					
						
							|  |  |  |     // Windows can easily distinguish between SO and SigSegV,
 | 
					
						
							|  |  |  |     // but SigInt, SigTerm, etc are handled differently.
 | 
					
						
							| 
									
										
										
										
											2017-09-07 16:51:33 +02:00
										 |  |  |     static SignalDefs signalDefs[] = { | 
					
						
							| 
									
										
										
										
											2019-03-19 23:55:18 +00:00
										 |  |  |         { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),  "SIGILL - Illegal instruction signal" }, | 
					
						
							|  |  |  |         { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" }, | 
					
						
							|  |  |  |         { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" }, | 
					
						
							|  |  |  |         { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { | 
					
						
							| 
									
										
										
										
											2017-08-29 14:02:14 +02:00
										 |  |  |         for (auto const& def : signalDefs) { | 
					
						
							|  |  |  |             if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { | 
					
						
							|  |  |  |                 reportFatal(def.name); | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // If its not an exception we care about, pass it along.
 | 
					
						
							|  |  |  |         // This stops us from eating debugger breaks etc.
 | 
					
						
							|  |  |  |         return EXCEPTION_CONTINUE_SEARCH; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FatalConditionHandler::FatalConditionHandler() { | 
					
						
							|  |  |  |         isSet = true; | 
					
						
							|  |  |  |         // 32k seems enough for Catch to handle stack overflow,
 | 
					
						
							|  |  |  |         // but the value was found experimentally, so there is no strong guarantee
 | 
					
						
							|  |  |  |         guaranteeSize = 32 * 1024; | 
					
						
							|  |  |  |         exceptionHandlerHandle = nullptr; | 
					
						
							|  |  |  |         // Register as first handler in current chain
 | 
					
						
							|  |  |  |         exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); | 
					
						
							|  |  |  |         // Pass in guarantee size to be filled
 | 
					
						
							|  |  |  |         SetThreadStackGuarantee(&guaranteeSize); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void FatalConditionHandler::reset() { | 
					
						
							|  |  |  |         if (isSet) { | 
					
						
							|  |  |  |             RemoveVectoredExceptionHandler(exceptionHandlerHandle); | 
					
						
							|  |  |  |             SetThreadStackGuarantee(&guaranteeSize); | 
					
						
							|  |  |  |             exceptionHandlerHandle = nullptr; | 
					
						
							|  |  |  |             isSet = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FatalConditionHandler::~FatalConditionHandler() { | 
					
						
							|  |  |  |         reset(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FatalConditionHandler::isSet = false; | 
					
						
							|  |  |  | ULONG FatalConditionHandler::guaranteeSize = 0; | 
					
						
							|  |  |  | PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Catch
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 14:56:07 +01:00
										 |  |  | #elif defined( CATCH_CONFIG_POSIX_SIGNALS )
 | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     struct SignalDefs { | 
					
						
							|  |  |  |         int id; | 
					
						
							|  |  |  |         const char* name; | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-05-27 18:12:40 +02:00
										 |  |  |      | 
					
						
							|  |  |  |     // 32kb for the alternate stack seems to be sufficient. However, this value
 | 
					
						
							|  |  |  |     // is experimentally determined, so that's not guaranteed.
 | 
					
						
							| 
									
										
										
										
											2019-08-06 19:17:16 -05:00
										 |  |  |     static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ; | 
					
						
							| 
									
										
										
										
											2018-05-27 18:12:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-07 16:51:33 +02:00
										 |  |  |     static SignalDefs signalDefs[] = { | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |         { SIGINT,  "SIGINT - Terminal interrupt signal" }, | 
					
						
							|  |  |  |         { SIGILL,  "SIGILL - Illegal instruction signal" }, | 
					
						
							|  |  |  |         { SIGFPE,  "SIGFPE - Floating point error signal" }, | 
					
						
							|  |  |  |         { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, | 
					
						
							|  |  |  |         { SIGTERM, "SIGTERM - Termination request signal" }, | 
					
						
							|  |  |  |         { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void FatalConditionHandler::handleSignal( int sig ) { | 
					
						
							| 
									
										
										
										
											2017-08-31 11:46:37 +02:00
										 |  |  |         char const * name = "<unknown signal>"; | 
					
						
							| 
									
										
										
										
											2017-08-29 14:02:14 +02:00
										 |  |  |         for (auto const& def : signalDefs) { | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |             if (sig == def.id) { | 
					
						
							|  |  |  |                 name = def.name; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         reset(); | 
					
						
							|  |  |  |         reportFatal(name); | 
					
						
							|  |  |  |         raise( sig ); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FatalConditionHandler::FatalConditionHandler() { | 
					
						
							|  |  |  |         isSet = true; | 
					
						
							|  |  |  |         stack_t sigStack; | 
					
						
							|  |  |  |         sigStack.ss_sp = altStackMem; | 
					
						
							| 
									
										
										
										
											2018-05-27 18:12:40 +02:00
										 |  |  |         sigStack.ss_size = sigStackSize; | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  |         sigStack.ss_flags = 0; | 
					
						
							|  |  |  |         sigaltstack(&sigStack, &oldSigStack); | 
					
						
							|  |  |  |         struct sigaction sa = { }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         sa.sa_handler = handleSignal; | 
					
						
							|  |  |  |         sa.sa_flags = SA_ONSTACK; | 
					
						
							|  |  |  |         for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { | 
					
						
							|  |  |  |             sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     FatalConditionHandler::~FatalConditionHandler() { | 
					
						
							|  |  |  |         reset(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void FatalConditionHandler::reset() { | 
					
						
							|  |  |  |         if( isSet ) { | 
					
						
							|  |  |  |             // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
 | 
					
						
							|  |  |  |             for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { | 
					
						
							|  |  |  |                 sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             // Return the old stack
 | 
					
						
							|  |  |  |             sigaltstack(&oldSigStack, nullptr); | 
					
						
							|  |  |  |             isSet = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool FatalConditionHandler::isSet = false; | 
					
						
							|  |  |  |     struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; | 
					
						
							|  |  |  |     stack_t FatalConditionHandler::oldSigStack = {}; | 
					
						
							| 
									
										
										
										
											2018-05-27 18:12:40 +02:00
										 |  |  |     char FatalConditionHandler::altStackMem[sigStackSize] = {}; | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Catch
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 14:56:07 +01:00
										 |  |  | #else
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Catch { | 
					
						
							|  |  |  |     void FatalConditionHandler::reset() {} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-06 22:28:42 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 14:56:07 +01:00
										 |  |  | #endif // signals/SEH handling
 | 
					
						
							| 
									
										
										
										
											2017-11-21 15:23:30 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #if defined(__GNUC__)
 | 
					
						
							|  |  |  | #    pragma GCC diagnostic pop
 | 
					
						
							|  |  |  | #endif
 |