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