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