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 <windows.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 unsigned ValidJumpBuffer : 1; 45 46 public: 47 CrashRecoveryContextImpl(CrashRecoveryContext *CRC) 48 : CRC(CRC), Failed(false), SwitchedThread(false), ValidJumpBuffer(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 if (ValidJumpBuffer) 84 longjmp(JumpBuffer, 1); 85 86 // Otherwise let the caller decide of the outcome of the crash. Currently 87 // this occurs when using SEH on Windows with MSVC or clang-cl. 88 } 89 }; 90 } 91 92 static ManagedStatic<std::mutex> gCrashRecoveryContextMutex; 93 static bool gCrashRecoveryEnabled = false; 94 95 static ManagedStatic<sys::ThreadLocal<const CrashRecoveryContext>> 96 tlIsRecoveringFromCrash; 97 98 static void installExceptionOrSignalHandlers(); 99 static void uninstallExceptionOrSignalHandlers(); 100 101 CrashRecoveryContextCleanup::~CrashRecoveryContextCleanup() {} 102 103 CrashRecoveryContext::~CrashRecoveryContext() { 104 // Reclaim registered resources. 105 CrashRecoveryContextCleanup *i = head; 106 const CrashRecoveryContext *PC = tlIsRecoveringFromCrash->get(); 107 tlIsRecoveringFromCrash->set(this); 108 while (i) { 109 CrashRecoveryContextCleanup *tmp = i; 110 i = tmp->next; 111 tmp->cleanupFired = true; 112 tmp->recoverResources(); 113 delete tmp; 114 } 115 tlIsRecoveringFromCrash->set(PC); 116 117 CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; 118 delete CRCI; 119 } 120 121 bool CrashRecoveryContext::isRecoveringFromCrash() { 122 return tlIsRecoveringFromCrash->get() != nullptr; 123 } 124 125 CrashRecoveryContext *CrashRecoveryContext::GetCurrent() { 126 if (!gCrashRecoveryEnabled) 127 return nullptr; 128 129 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 130 if (!CRCI) 131 return nullptr; 132 133 return CRCI->CRC; 134 } 135 136 void CrashRecoveryContext::Enable() { 137 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); 138 // FIXME: Shouldn't this be a refcount or something? 139 if (gCrashRecoveryEnabled) 140 return; 141 gCrashRecoveryEnabled = true; 142 installExceptionOrSignalHandlers(); 143 } 144 145 void CrashRecoveryContext::Disable() { 146 std::lock_guard<std::mutex> L(*gCrashRecoveryContextMutex); 147 if (!gCrashRecoveryEnabled) 148 return; 149 gCrashRecoveryEnabled = false; 150 uninstallExceptionOrSignalHandlers(); 151 } 152 153 void CrashRecoveryContext::registerCleanup(CrashRecoveryContextCleanup *cleanup) 154 { 155 if (!cleanup) 156 return; 157 if (head) 158 head->prev = cleanup; 159 cleanup->next = head; 160 head = cleanup; 161 } 162 163 void 164 CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { 165 if (!cleanup) 166 return; 167 if (cleanup == head) { 168 head = cleanup->next; 169 if (head) 170 head->prev = nullptr; 171 } 172 else { 173 cleanup->prev->next = cleanup->next; 174 if (cleanup->next) 175 cleanup->next->prev = cleanup->prev; 176 } 177 delete cleanup; 178 } 179 180 #if defined(_MSC_VER) 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 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 "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 // Handle the crash 284 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( 285 (int)ExceptionInfo->ExceptionRecord->ExceptionCode, 286 reinterpret_cast<uintptr_t>(ExceptionInfo)); 287 288 // Note that we don't actually get here because HandleCrash calls 289 // longjmp, which means the HandleCrash function never returns. 290 llvm_unreachable("Handled the crash, should have longjmp'ed out of here"); 291 } 292 293 // Because the Enable and Disable calls are static, it means that 294 // there may not actually be an Impl available, or even a current 295 // CrashRecoveryContext at all. So we make use of a thread-local 296 // exception table. The handles contained in here will either be 297 // non-NULL, valid VEH handles, or NULL. 298 static sys::ThreadLocal<const void> sCurrentExceptionHandle; 299 300 static void installExceptionOrSignalHandlers() { 301 // We can set up vectored exception handling now. We will install our 302 // handler as the front of the list, though there's no assurances that 303 // it will remain at the front (another call could install itself before 304 // our handler). This 1) isn't likely, and 2) shouldn't cause problems. 305 PVOID handle = ::AddVectoredExceptionHandler(1, ExceptionHandler); 306 sCurrentExceptionHandle.set(handle); 307 } 308 309 static void uninstallExceptionOrSignalHandlers() { 310 PVOID currentHandle = const_cast<PVOID>(sCurrentExceptionHandle.get()); 311 if (currentHandle) { 312 // Now we can remove the vectored exception handler from the chain 313 ::RemoveVectoredExceptionHandler(currentHandle); 314 315 // Reset the handle in our thread-local set. 316 sCurrentExceptionHandle.set(NULL); 317 } 318 } 319 320 #else // !_WIN32 321 322 // Generic POSIX implementation. 323 // 324 // This implementation relies on synchronous signals being delivered to the 325 // current thread. We use a thread local object to keep track of the active 326 // crash recovery context, and install signal handlers to invoke HandleCrash on 327 // the active object. 328 // 329 // This implementation does not attempt to chain signal handlers in any 330 // reliable fashion -- if we get a signal outside of a crash recovery context we 331 // simply disable crash recovery and raise the signal again. 332 333 #include <signal.h> 334 335 static const int Signals[] = 336 { SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP }; 337 static const unsigned NumSignals = array_lengthof(Signals); 338 static struct sigaction PrevActions[NumSignals]; 339 340 static void CrashRecoverySignalHandler(int Signal) { 341 // Lookup the current thread local recovery object. 342 const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); 343 344 if (!CRCI) { 345 // We didn't find a crash recovery context -- this means either we got a 346 // signal on a thread we didn't expect it on, the application got a signal 347 // outside of a crash recovery context, or something else went horribly 348 // wrong. 349 // 350 // Disable crash recovery and raise the signal again. The assumption here is 351 // that the enclosing application will terminate soon, and we won't want to 352 // attempt crash recovery again. 353 // 354 // This call of Disable isn't thread safe, but it doesn't actually matter. 355 CrashRecoveryContext::Disable(); 356 raise(Signal); 357 358 // The signal will be thrown once the signal mask is restored. 359 return; 360 } 361 362 // Unblock the signal we received. 363 sigset_t SigMask; 364 sigemptyset(&SigMask); 365 sigaddset(&SigMask, Signal); 366 sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); 367 368 // As per convention, -2 indicates a crash or timeout as opposed to failure to 369 // execute (see llvm/include/llvm/Support/Program.h) 370 int RetCode = -2; 371 372 // Don't consider a broken pipe as a crash (see clang/lib/Driver/Driver.cpp) 373 if (Signal == SIGPIPE) 374 RetCode = EX_IOERR; 375 376 if (CRCI) 377 const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash(RetCode, Signal); 378 } 379 380 static void installExceptionOrSignalHandlers() { 381 // Setup the signal handler. 382 struct sigaction Handler; 383 Handler.sa_handler = CrashRecoverySignalHandler; 384 Handler.sa_flags = 0; 385 sigemptyset(&Handler.sa_mask); 386 387 for (unsigned i = 0; i != NumSignals; ++i) { 388 sigaction(Signals[i], &Handler, &PrevActions[i]); 389 } 390 } 391 392 static void uninstallExceptionOrSignalHandlers() { 393 // Restore the previous signal handlers. 394 for (unsigned i = 0; i != NumSignals; ++i) 395 sigaction(Signals[i], &PrevActions[i], nullptr); 396 } 397 398 #endif // !_WIN32 399 400 bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { 401 // If crash recovery is disabled, do nothing. 402 if (gCrashRecoveryEnabled) { 403 assert(!Impl && "Crash recovery context already initialized!"); 404 CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); 405 Impl = CRCI; 406 407 CRCI->ValidJumpBuffer = true; 408 if (setjmp(CRCI->JumpBuffer) != 0) { 409 return false; 410 } 411 } 412 413 Fn(); 414 return true; 415 } 416 417 #endif // !_MSC_VER 418 419 // FIXME: Portability. 420 static void setThreadBackgroundPriority() { 421 #ifdef __APPLE__ 422 setpriority(PRIO_DARWIN_THREAD, 0, PRIO_DARWIN_BG); 423 #endif 424 } 425 426 static bool hasThreadBackgroundPriority() { 427 #ifdef __APPLE__ 428 return getpriority(PRIO_DARWIN_THREAD, 0) == 1; 429 #else 430 return false; 431 #endif 432 } 433 434 namespace { 435 struct RunSafelyOnThreadInfo { 436 function_ref<void()> Fn; 437 CrashRecoveryContext *CRC; 438 bool UseBackgroundPriority; 439 bool Result; 440 }; 441 } 442 443 static void RunSafelyOnThread_Dispatch(void *UserData) { 444 RunSafelyOnThreadInfo *Info = 445 reinterpret_cast<RunSafelyOnThreadInfo*>(UserData); 446 447 if (Info->UseBackgroundPriority) 448 setThreadBackgroundPriority(); 449 450 Info->Result = Info->CRC->RunSafely(Info->Fn); 451 } 452 bool CrashRecoveryContext::RunSafelyOnThread(function_ref<void()> Fn, 453 unsigned RequestedStackSize) { 454 bool UseBackgroundPriority = hasThreadBackgroundPriority(); 455 RunSafelyOnThreadInfo Info = { Fn, this, UseBackgroundPriority, false }; 456 llvm_execute_on_thread(RunSafelyOnThread_Dispatch, &Info, 457 RequestedStackSize == 0 458 ? llvm::None 459 : llvm::Optional<unsigned>(RequestedStackSize)); 460 if (CrashRecoveryContextImpl *CRC = (CrashRecoveryContextImpl *)Impl) 461 CRC->setSwitchedThread(); 462 return Info.Result; 463 } 464