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