1 //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
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 "gwp_asan/common.h"
10 #include "gwp_asan/crash_handler.h"
11 #include "gwp_asan/guarded_pool_allocator.h"
12 #include "gwp_asan/optional/segv_handler.h"
13 #include "gwp_asan/options.h"
14 
15 #include <assert.h>
16 #include <inttypes.h>
17 #include <signal.h>
18 #include <stdio.h>
19 
20 namespace {
21 using gwp_asan::AllocationMetadata;
22 using gwp_asan::Error;
23 using gwp_asan::GuardedPoolAllocator;
24 using gwp_asan::crash_handler::PrintBacktrace_t;
25 using gwp_asan::crash_handler::Printf_t;
26 using gwp_asan::crash_handler::SegvBacktrace_t;
27 
28 struct sigaction PreviousHandler;
29 bool SignalHandlerInstalled;
30 gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
31 Printf_t PrintfForSignalHandler;
32 PrintBacktrace_t PrintBacktraceForSignalHandler;
33 SegvBacktrace_t BacktraceForSignalHandler;
34 
sigSegvHandler(int sig,siginfo_t * info,void * ucontext)35 static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
36   if (GPAForSignalHandler) {
37     GPAForSignalHandler->stop();
38 
39     gwp_asan::crash_handler::dumpReport(
40         reinterpret_cast<uintptr_t>(info->si_addr),
41         GPAForSignalHandler->getAllocatorState(),
42         GPAForSignalHandler->getMetadataRegion(), BacktraceForSignalHandler,
43         PrintfForSignalHandler, PrintBacktraceForSignalHandler, ucontext);
44   }
45 
46   // Process any previous handlers.
47   if (PreviousHandler.sa_flags & SA_SIGINFO) {
48     PreviousHandler.sa_sigaction(sig, info, ucontext);
49   } else if (PreviousHandler.sa_handler == SIG_DFL) {
50     // If the previous handler was the default handler, cause a core dump.
51     signal(SIGSEGV, SIG_DFL);
52     raise(SIGSEGV);
53   } else if (PreviousHandler.sa_handler == SIG_IGN) {
54     // If the previous segv handler was SIGIGN, crash iff we were responsible
55     // for the crash.
56     if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
57                                  reinterpret_cast<uintptr_t>(info->si_addr))) {
58       signal(SIGSEGV, SIG_DFL);
59       raise(SIGSEGV);
60     }
61   } else {
62     PreviousHandler.sa_handler(sig);
63   }
64 }
65 
66 struct ScopedEndOfReportDecorator {
ScopedEndOfReportDecorator__anon831539b40111::ScopedEndOfReportDecorator67   ScopedEndOfReportDecorator(gwp_asan::crash_handler::Printf_t Printf)
68       : Printf(Printf) {}
~ScopedEndOfReportDecorator__anon831539b40111::ScopedEndOfReportDecorator69   ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
70   gwp_asan::crash_handler::Printf_t Printf;
71 };
72 
73 // Prints the provided error and metadata information.
printHeader(Error E,uintptr_t AccessPtr,const gwp_asan::AllocationMetadata * Metadata,Printf_t Printf)74 void printHeader(Error E, uintptr_t AccessPtr,
75                  const gwp_asan::AllocationMetadata *Metadata,
76                  Printf_t Printf) {
77   // Print using intermediate strings. Platforms like Android don't like when
78   // you print multiple times to the same line, as there may be a newline
79   // appended to a log file automatically per Printf() call.
80   constexpr size_t kDescriptionBufferLen = 128;
81   char DescriptionBuffer[kDescriptionBufferLen] = "";
82   if (E != Error::UNKNOWN && Metadata != nullptr) {
83     uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
84     size_t Size = __gwp_asan_get_allocation_size(Metadata);
85     if (E == Error::USE_AFTER_FREE) {
86       snprintf(DescriptionBuffer, kDescriptionBufferLen,
87                "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
88                AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
89                Address);
90     } else if (AccessPtr < Address) {
91       snprintf(DescriptionBuffer, kDescriptionBufferLen,
92                "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
93                Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
94                Address);
95     } else if (AccessPtr > Address) {
96       snprintf(DescriptionBuffer, kDescriptionBufferLen,
97                "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
98                AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
99                Address);
100     } else {
101       snprintf(DescriptionBuffer, kDescriptionBufferLen,
102                "(a %zu-byte allocation) ", Size);
103     }
104   }
105 
106   // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
107   // a null terminator, and round to the nearest 8-byte boundary.
108   uint64_t ThreadID = gwp_asan::getThreadID();
109   constexpr size_t kThreadBufferLen = 24;
110   char ThreadBuffer[kThreadBufferLen];
111   if (ThreadID == gwp_asan::kInvalidThreadID)
112     snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
113   else
114     snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
115 
116   Printf("%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
117          AccessPtr, DescriptionBuffer, ThreadBuffer);
118 }
119 
defaultPrintStackTrace(uintptr_t * Trace,size_t TraceLength,gwp_asan::crash_handler::Printf_t Printf)120 void defaultPrintStackTrace(uintptr_t *Trace, size_t TraceLength,
121                             gwp_asan::crash_handler::Printf_t Printf) {
122   if (TraceLength == 0)
123     Printf("  <unknown (does your allocator support backtracing?)>\n");
124 
125   for (size_t i = 0; i < TraceLength; ++i) {
126     Printf("  #%zu 0x%zx in <unknown>\n", i, Trace[i]);
127   }
128   Printf("\n");
129 }
130 
131 } // anonymous namespace
132 
133 namespace gwp_asan {
134 namespace crash_handler {
getBasicPrintBacktraceFunction()135 PrintBacktrace_t getBasicPrintBacktraceFunction() {
136   return defaultPrintStackTrace;
137 }
138 
installSignalHandlers(gwp_asan::GuardedPoolAllocator * GPA,Printf_t Printf,PrintBacktrace_t PrintBacktrace,SegvBacktrace_t SegvBacktrace)139 void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
140                            PrintBacktrace_t PrintBacktrace,
141                            SegvBacktrace_t SegvBacktrace) {
142   GPAForSignalHandler = GPA;
143   PrintfForSignalHandler = Printf;
144   PrintBacktraceForSignalHandler = PrintBacktrace;
145   BacktraceForSignalHandler = SegvBacktrace;
146 
147   struct sigaction Action = {};
148   Action.sa_sigaction = sigSegvHandler;
149   Action.sa_flags = SA_SIGINFO;
150   sigaction(SIGSEGV, &Action, &PreviousHandler);
151   SignalHandlerInstalled = true;
152 }
153 
uninstallSignalHandlers()154 void uninstallSignalHandlers() {
155   if (SignalHandlerInstalled) {
156     sigaction(SIGSEGV, &PreviousHandler, nullptr);
157     SignalHandlerInstalled = false;
158   }
159 }
160 
dumpReport(uintptr_t ErrorPtr,const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,SegvBacktrace_t SegvBacktrace,Printf_t Printf,PrintBacktrace_t PrintBacktrace,void * Context)161 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
162                 const gwp_asan::AllocationMetadata *Metadata,
163                 SegvBacktrace_t SegvBacktrace, Printf_t Printf,
164                 PrintBacktrace_t PrintBacktrace, void *Context) {
165   assert(State && "dumpReport missing Allocator State.");
166   assert(Metadata && "dumpReport missing Metadata.");
167   assert(Printf && "dumpReport missing Printf.");
168 
169   if (!__gwp_asan_error_is_mine(State, ErrorPtr))
170     return;
171 
172   Printf("*** GWP-ASan detected a memory error ***\n");
173   ScopedEndOfReportDecorator Decorator(Printf);
174 
175   uintptr_t InternalErrorPtr = __gwp_asan_get_internal_crash_address(State);
176   if (InternalErrorPtr != 0u)
177     ErrorPtr = InternalErrorPtr;
178 
179   Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
180 
181   if (E == Error::UNKNOWN) {
182     Printf("GWP-ASan cannot provide any more information about this error. "
183            "This may occur due to a wild memory access into the GWP-ASan pool, "
184            "or an overflow/underflow that is > 512B in length.\n");
185     return;
186   }
187 
188   const gwp_asan::AllocationMetadata *AllocMeta =
189       __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
190 
191   // Print the error header.
192   printHeader(E, ErrorPtr, AllocMeta, Printf);
193 
194   // Print the fault backtrace.
195   static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
196   uintptr_t Trace[kMaximumStackFramesForCrashTrace];
197   size_t TraceLength =
198       SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
199 
200   PrintBacktrace(Trace, TraceLength, Printf);
201 
202   if (AllocMeta == nullptr)
203     return;
204 
205   // Maybe print the deallocation trace.
206   if (__gwp_asan_is_deallocated(AllocMeta)) {
207     uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
208     if (ThreadID == kInvalidThreadID)
209       Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
210     else
211       Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
212     TraceLength = __gwp_asan_get_deallocation_trace(
213         AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
214     PrintBacktrace(Trace, TraceLength, Printf);
215   }
216 
217   // Print the allocation trace.
218   uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
219   if (ThreadID == kInvalidThreadID)
220     Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
221   else
222     Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
223   TraceLength = __gwp_asan_get_allocation_trace(
224       AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
225   PrintBacktrace(Trace, TraceLength, Printf);
226 }
227 } // namespace crash_handler
228 } // namespace gwp_asan
229