xref: /freebsd/contrib/llvm-project/llvm/lib/Support/CrashRecoveryContext.cpp (revision 85868e8a1daeaae7a0e48effb2ea2310ae3b02c6)
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