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