1 //===--- CrashRecoveryContext.cpp - Crash Recovery ------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Support/CrashRecoveryContext.h" 10 #include "llvm/Config/llvm-config.h" 11 #include "llvm/Support/ErrorHandling.h" 12 #include "llvm/Support/ManagedStatic.h" 13 #include "llvm/Support/Signals.h" 14 #include "llvm/Support/ThreadLocal.h" 15 #include <mutex> 16 #include <setjmp.h> 17 #ifdef _WIN32 18 #include <excpt.h> // for GetExceptionInformation 19 #endif 20 #if LLVM_ON_UNIX 21 #include <sysexits.h> // EX_IOERR 22 #endif 23 24 using namespace llvm; 25 26 namespace { 27 28 struct CrashRecoveryContextImpl; 29 30 static ManagedStatic< 31 sys::ThreadLocal<const CrashRecoveryContextImpl> > CurrentContext; 32 33 struct CrashRecoveryContextImpl { 34 // When threads are disabled, this links up all active 35 // CrashRecoveryContextImpls. When threads are enabled there's one thread 36 // per CrashRecoveryContext and CurrentContext is a thread-local, so only one 37 // CrashRecoveryContextImpl is active per thread and this is always null. 38 const CrashRecoveryContextImpl *Next; 39 40 CrashRecoveryContext *CRC; 41 ::jmp_buf JumpBuffer; 42 volatile unsigned Failed : 1; 43 unsigned SwitchedThread : 1; 44 45 public: 46 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), 47 Failed(false), 48 SwitchedThread(false) { 49 Next = CurrentContext->get(); 50 CurrentContext->set(this); 51 } 52 ~CrashRecoveryContextImpl() { 53 if (!SwitchedThread) 54 CurrentContext->set(Next); 55 } 56 57 /// Called when the separate crash-recovery thread was finished, to 58 /// indicate that we don't need to clear the thread-local CurrentContext. 59 void setSwitchedThread() { 60 #if defined(LLVM_ENABLE_THREADS) && LLVM_ENABLE_THREADS != 0 61 SwitchedThread = true; 62 #endif 63 } 64 65 // If the function ran by the CrashRecoveryContext crashes or fails, then 66 // 'RetCode' represents the returned error code, as if it was returned by a 67 // process. 'Context' represents the signal type on Unix; on Windows, it is 68 // the ExceptionContext. 69 void HandleCrash(int RetCode, uintptr_t Context) { 70 // Eliminate the current context entry, to avoid re-entering in case the 71 // cleanup code crashes. 72 CurrentContext->set(Next); 73 74 assert(!Failed && "Crash recovery context already failed!"); 75 Failed = true; 76 77 if (CRC->DumpStackAndCleanupOnFailure) 78 sys::CleanupOnSignal(Context); 79 80 CRC->RetCode = RetCode; 81 82 // Jump back to the RunSafely we were called under. 83 longjmp(JumpBuffer, 1); 84 } 85 }; 86 87 } 88 89 static ManagedStatic<std::mutex> gCrashRecoveryContextMutex; 90 static bool gCrashRecoveryEnabled = false; 91 92 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>> 93 tlIsRecoveringFromCrash; 94 95 static void installExceptionOrSignalHandlers(); 96 static void uninstallExceptionOrSignalHandlers(); 97 98 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} 99 100 CrashRecoveryContext::~CrashRecoveryContext() { 101 // Reclaim registered resources. 102 CrashRecoveryContextCleanup *i = head; 103 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get(); 104 tlIsRecoveringFromCrash->set(this); 105 while (i) { 106 CrashRecoveryContextCleanup *tmp = i; 107 i = tmp->next; 108 tmp->cleanupFired = true; 109 tmp->recoverResources(); 110 delete tmp; 111 } 112 tlIsRecoveringFromCrash->set(PC); 113 114 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 115 delete CRCI; 116 } 117 118 bool CrashRecoveryContext::isRecoveringFromCrash() { 119 return tlIsRecoveringFromCrash->get() != nullptr; 120 } 121 122 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { 123 if (!gCrashRecoveryEnabled) 124 return nullptr; 125 126 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 127 if (!CRCI) 128 return nullptr; 129 130 return CRCI->CRC; 131 } 132 133 void CrashRecoveryContext::Enable() { 134 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); 135 // FIXME: Shouldn't this be a refcount or something? 136 if (gCrashRecoveryEnabled) 137 return; 138 gCrashRecoveryEnabled = true; 139 installExceptionOrSignalHandlers(); 140 } 141 142 void CrashRecoveryContext::Disable() { 143 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); 144 if (!gCrashRecoveryEnabled) 145 return; 146 gCrashRecoveryEnabled = false; 147 uninstallExceptionOrSignalHandlers(); 148 } 149 150 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) 151 { 152 if (!cleanup) 153 return; 154 if (head) 155 head->prev = cleanup; 156 cleanup->next = head; 157 head = cleanup; 158 } 159 160 void 161 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { 162 if (!cleanup) 163 return; 164 if (cleanup == head) { 165 head = cleanup->next; 166 if (head) 167 head->prev = nullptr; 168 } 169 else { 170 cleanup->prev->next = cleanup->next; 171 if (cleanup->next) 172 cleanup->next->prev = cleanup->prev; 173 } 174 delete cleanup; 175 } 176 177 #if defined(_MSC_VER) 178 // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way 179 // better than VEH. Vectored exception handling catches all exceptions happening 180 // on the thread with installed exception handlers, so it can interfere with 181 // internal exception handling of other libraries on that thread. SEH works 182 // exactly as you would expect normal exception handling to work: it only 183 // catches exceptions if they would bubble out from the stack frame with __try / 184 // __except. 185 186 static void installExceptionOrSignalHandlers() {} 187 static void uninstallExceptionOrSignalHandlers() {} 188 189 // We need this function because the call to GetExceptionInformation() can only 190 // occur inside the __except evaluation block 191 static int ExceptionFilter(bool DumpStackAndCleanup, 192 _EXCEPTION_POINTERS *Except) { 193 if (DumpStackAndCleanup) 194 sys::CleanupOnSignal((uintptr_t)Except); 195 return EXCEPTION_EXECUTE_HANDLER; 196 } 197 198 static bool InvokeFunctionCall(function_ref<void()> Fn, 199 bool DumpStackAndCleanup, int &RetCode) { 200 __try { 201 Fn(); 202 } __except (ExceptionFilter(DumpStackAndCleanup, GetExceptionInformation())) { 203 RetCode = GetExceptionCode(); 204 return false; 205 } 206 return true; 207 } 208 209 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { 210 if (!gCrashRecoveryEnabled) { 211 Fn(); 212 return true; 213 } 214 return InvokeFunctionCall(Fn, DumpStackAndCleanupOnFailure, RetCode); 215 } 216 217 #else // !_MSC_VER 218 219 #if defined(_WIN32) 220 // This is a non-MSVC compiler, probably mingw gcc or clang without 221 // -fms-extensions. Use vectored exception handling (VEH). 222 // 223 // On Windows, we can make use of vectored exception handling to catch most 224 // crashing situations. Note that this does mean we will be alerted of 225 // exceptions *before* structured exception handling has the opportunity to 226 // catch it. Unfortunately, this causes problems in practice with other code 227 // running on threads with LLVM crash recovery contexts, so we would like to 228 // eventually move away from VEH. 229 // 230 // Vectored works on a per-thread basis, which is an advantage over 231 // SetUnhandledExceptionFilter. SetUnhandledExceptionFilter also doesn't have 232 // any native support for chaining exception handlers, but VEH allows more than 233 // one. 234 // 235 // The vectored exception handler functionality was added in Windows 236 // XP, so if support for older versions of Windows is required, 237 // it will have to be added. 238 239 #include "Windows/WindowsSupport.h" 240 241 static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) 242 { 243 // DBG_PRINTEXCEPTION_WIDE_C is not properly defined on all supported 244 // compilers and platforms, so we define it manually. 245 constexpr ULONG DbgPrintExceptionWideC = 0x4001000AL; 246 switch (ExceptionInfo->ExceptionRecord->ExceptionCode) 247 { 248 case DBG_PRINTEXCEPTION_C: 249 case DbgPrintExceptionWideC: 250 case 0x406D1388: // set debugger thread name 251 return EXCEPTION_CONTINUE_EXECUTION; 252 } 253 254 // Lookup the current thread local recovery object. 255 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 256 257 if (!CRCI) { 258 // Something has gone horribly wrong, so let's just tell everyone 259 // to keep searching 260 CrashRecoveryContext::Disable(); 261 return EXCEPTION_CONTINUE_SEARCH; 262 } 263 264 // TODO: We can capture the stack backtrace here and store it on the 265 // implementation if we so choose. 266 267 // Handle the crash 268 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( 269 (int)ExceptionInfo->ExceptionRecord->ExceptionCode, 270 reinterpret_cast<uintptr_t>(ExceptionInfo)); 271 272 // Note that we don't actually get here because HandleCrash calls 273 // longjmp, which means the HandleCrash function never returns. 274 llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); 275 } 276 277 // Because the Enable and Disable calls are static, it means that 278 // there may not actually be an Impl available, or even a current 279 // CrashRecoveryContext at all. So we make use of a thread-local 280 // exception table. The handles contained in here will either be 281 // non-NULL, valid VEH handles, or NULL. 282 static sys::ThreadLocal<const void> sCurrentExceptionHandle; 283 284 static void installExceptionOrSignalHandlers() { 285 // We can set up vectored exception handling now. We will install our 286 // handler as the front of the list, though there's no assurances that 287 // it will remain at the front (another call could install itself before 288 // our handler). This 1) isn't likely, and 2) shouldn't cause problems. 289 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); 290 sCurrentExceptionHandle.set(handle); 291 } 292 293 static void uninstallExceptionOrSignalHandlers() { 294 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get()); 295 if (currentHandle) { 296 // Now we can remove the vectored exception handler from the chain 297 ::RemoveVectoredExceptionHandler(currentHandle); 298 299 // Reset the handle in our thread-local set. 300 sCurrentExceptionHandle.set(NULL); 301 } 302 } 303 304 #else // !_WIN32 305 306 // Generic POSIX implementation. 307 // 308 // This implementation relies on synchronous signals being delivered to the 309 // current thread. We use a thread local object to keep track of the active 310 // crash recovery context, and install signal handlers to invoke HandleCrash on 311 // the active object. 312 // 313 // This implementation does not attempt to chain signal handlers in any 314 // reliable fashion -- if we get a signal outside of a crash recovery context we 315 // simply disable crash recovery and raise the signal again. 316 317 #include <signal.h> 318 319 static const int Signals[] = 320 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; 321 static const unsigned NumSignals = array_lengthof(Signals); 322 static struct sigaction PrevActions[NumSignals]; 323 324 static void CrashRecoverySignalHandler(int Signal) { 325 // Lookup the current thread local recovery object. 326 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 327 328 if (!CRCI) { 329 // We didn't find a crash recovery context -- this means either we got a 330 // signal on a thread we didn't expect it on, the application got a signal 331 // outside of a crash recovery context, or something else went horribly 332 // wrong. 333 // 334 // Disable crash recovery and raise the signal again. The assumption here is 335 // that the enclosing application will terminate soon, and we won't want to 336 // attempt crash recovery again. 337 // 338 // This call of Disable isn't thread safe, but it doesn't actually matter. 339 CrashRecoveryContext::Disable(); 340 raise(Signal); 341 342 // The signal will be thrown once the signal mask is restored. 343 return; 344 } 345 346 // Unblock the signal we received. 347 sigset_t SigMask; 348 sigemptyset(&SigMask); 349 sigaddset(&SigMask, Signal); 350 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); 351 352 // As per convention, -2 indicates a crash or timeout as opposed to failure to 353 // execute (see llvm/include/llvm/Support/Program.h) 354 int RetCode = -2; 355 356 // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp) 357 if (Signal == SIGPIPE) 358 RetCode = EX_IOERR; 359 360 if (CRCI) 361 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal); 362 } 363 364 static void installExceptionOrSignalHandlers() { 365 // Setup the signal handler. 366 struct sigaction Handler; 367 Handler.sa_handler = CrashRecoverySignalHandler; 368 Handler.sa_flags = 0; 369 sigemptyset(&Handler.sa_mask); 370 371 for (unsigned i = 0; i != NumSignals; ++i) { 372 sigaction(Signals[i], &Handler, &PrevActions[i]); 373 } 374 } 375 376 static void uninstallExceptionOrSignalHandlers() { 377 // Restore the previous signal handlers. 378 for (unsigned i = 0; i != NumSignals; ++i) 379 sigaction(Signals[i], &PrevActions[i], nullptr); 380 } 381 382 #endif // !_WIN32 383 384 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { 385 // If crash recovery is disabled, do nothing. 386 if (gCrashRecoveryEnabled) { 387 assert(!Impl && "Crash recovery context already initialized!"); 388 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); 389 Impl = CRCI; 390 391 if (setjmp(CRCI->JumpBuffer) != 0) { 392 return false; 393 } 394 } 395 396 Fn(); 397 return true; 398 } 399 400 #endif // !_MSC_VER 401 402 void CrashRecoveryContext::HandleCrash() { 403 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 404 assert(CRCI && "Crash recovery context never initialized!"); 405 // As per convention, -2 indicates a crash or timeout as opposed to failure to 406 // execute (see llvm/include/llvm/Support/Program.h) 407 CRCI->HandleCrash(-2, 0); 408 } 409 410 // FIXME: Portability. 411 static void setThreadBackgroundPriority() { 412 #ifdef __APPLE__ 413 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); 414 #endif 415 } 416 417 static bool hasThreadBackgroundPriority() { 418 #ifdef __APPLE__ 419 return getpriority(PRIO_DARWIN_THREAD, 0) == 1; 420 #else 421 return false; 422 #endif 423 } 424 425 namespace { 426 struct RunSafelyOnThreadInfo { 427 function_ref<void()> Fn; 428 CrashRecoveryContext *CRC; 429 bool UseBackgroundPriority; 430 bool Result; 431 }; 432 } 433 434 static void RunSafelyOnThread_Dispatch(void *UserData) { 435 RunSafelyOnThreadInfo *Info = 436 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); 437 438 if (Info->UseBackgroundPriority) 439 setThreadBackgroundPriority(); 440 441 Info->Result = Info->CRC->RunSafely(Info->Fn); 442 } 443 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn, 444 unsigned RequestedStackSize) { 445 bool UseBackgroundPriority = hasThreadBackgroundPriority(); 446 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; 447 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, 448 RequestedStackSize == 0 449 ? llvm::None 450 : llvm::Optional<unsigned>(RequestedStackSize)); 451 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) 452 CRC->setSwitchedThread(); 453 return Info.Result; 454 } 455