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 // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
16 // macro is defined before including <inttypes.h>.
17 #ifndef __STDC_FORMAT_MACROS
18 #define __STDC_FORMAT_MACROS 1
19 #endif
20
21 #include <assert.h>
22 #include <inttypes.h>
23 #include <signal.h>
24 #include <stdio.h>
25
26 using gwp_asan::AllocationMetadata;
27 using gwp_asan::Error;
28 using gwp_asan::GuardedPoolAllocator;
29 using gwp_asan::Printf_t;
30 using gwp_asan::backtrace::PrintBacktrace_t;
31 using gwp_asan::backtrace::SegvBacktrace_t;
32
33 namespace {
34
35 struct ScopedEndOfReportDecorator {
ScopedEndOfReportDecorator__anon647644720111::ScopedEndOfReportDecorator36 ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
~ScopedEndOfReportDecorator__anon647644720111::ScopedEndOfReportDecorator37 ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
38 gwp_asan::Printf_t Printf;
39 };
40
41 // Prints the provided error and metadata information.
printHeader(Error E,uintptr_t AccessPtr,const gwp_asan::AllocationMetadata * Metadata,Printf_t Printf)42 void printHeader(Error E, uintptr_t AccessPtr,
43 const gwp_asan::AllocationMetadata *Metadata,
44 Printf_t Printf) {
45 // Print using intermediate strings. Platforms like Android don't like when
46 // you print multiple times to the same line, as there may be a newline
47 // appended to a log file automatically per Printf() call.
48 constexpr size_t kDescriptionBufferLen = 128;
49 char DescriptionBuffer[kDescriptionBufferLen] = "";
50
51 bool AccessWasInBounds = false;
52 if (E != Error::UNKNOWN && Metadata != nullptr) {
53 uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
54 size_t Size = __gwp_asan_get_allocation_size(Metadata);
55 if (AccessPtr < Address) {
56 snprintf(DescriptionBuffer, kDescriptionBufferLen,
57 "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
58 Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
59 Address);
60 } else if (AccessPtr > Address) {
61 snprintf(DescriptionBuffer, kDescriptionBufferLen,
62 "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
63 AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
64 Address);
65 } else if (E == Error::DOUBLE_FREE) {
66 snprintf(DescriptionBuffer, kDescriptionBufferLen,
67 "(a %zu-byte allocation) ", Size);
68 } else {
69 AccessWasInBounds = true;
70 snprintf(DescriptionBuffer, kDescriptionBufferLen,
71 "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
72 AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
73 Address);
74 }
75 }
76
77 // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
78 // a null terminator, and round to the nearest 8-byte boundary.
79 uint64_t ThreadID = gwp_asan::getThreadID();
80 constexpr size_t kThreadBufferLen = 24;
81 char ThreadBuffer[kThreadBufferLen];
82 if (ThreadID == gwp_asan::kInvalidThreadID)
83 snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
84 else
85 snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
86
87 const char *OutOfBoundsAndUseAfterFreeWarning = "";
88 if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {
89 OutOfBoundsAndUseAfterFreeWarning =
90 " (warning: buffer overflow/underflow detected on a free()'d "
91 "allocation. This either means you have a buffer-overflow and a "
92 "use-after-free at the same time, or you have a long-lived "
93 "use-after-free bug where the allocation/deallocation metadata below "
94 "has already been overwritten and is likely bogus)";
95 }
96
97 Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
98 OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,
99 ThreadBuffer);
100 }
101
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)102 void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
103 const gwp_asan::AllocationMetadata *Metadata,
104 SegvBacktrace_t SegvBacktrace, Printf_t Printf,
105 PrintBacktrace_t PrintBacktrace, void *Context) {
106 assert(State && "dumpReport missing Allocator State.");
107 assert(Metadata && "dumpReport missing Metadata.");
108 assert(Printf && "dumpReport missing Printf.");
109 assert(__gwp_asan_error_is_mine(State, ErrorPtr) &&
110 "dumpReport() called on a non-GWP-ASan error.");
111
112 uintptr_t InternalErrorPtr =
113 __gwp_asan_get_internal_crash_address(State, ErrorPtr);
114 if (InternalErrorPtr)
115 ErrorPtr = InternalErrorPtr;
116
117 const gwp_asan::AllocationMetadata *AllocMeta =
118 __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
119
120 // It's unusual for a signal handler to be invoked multiple times for the same
121 // allocation, but it's possible in various scenarios, like:
122 // 1. A double-free or invalid-free was invoked in one thread at the same
123 // time as a buffer-overflow or use-after-free in another thread, or
124 // 2. Two threads do a use-after-free or buffer-overflow at the same time.
125 // In these instances, we've already dumped a report for this allocation, so
126 // skip dumping this issue as well.
127 if (AllocMeta->HasCrashed)
128 return;
129
130 Printf("*** GWP-ASan detected a memory error ***\n");
131 ScopedEndOfReportDecorator Decorator(Printf);
132
133 Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
134 if (E == Error::UNKNOWN) {
135 Printf("GWP-ASan cannot provide any more information about this error. "
136 "This may occur due to a wild memory access into the GWP-ASan pool, "
137 "or an overflow/underflow that is > 512B in length.\n");
138 return;
139 }
140
141 // Print the error header.
142 printHeader(E, ErrorPtr, AllocMeta, Printf);
143
144 // Print the fault backtrace.
145 static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
146 uintptr_t Trace[kMaximumStackFramesForCrashTrace];
147 size_t TraceLength =
148 SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
149
150 PrintBacktrace(Trace, TraceLength, Printf);
151
152 if (AllocMeta == nullptr)
153 return;
154
155 // Maybe print the deallocation trace.
156 if (__gwp_asan_is_deallocated(AllocMeta)) {
157 uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
158 if (ThreadID == gwp_asan::kInvalidThreadID)
159 Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
160 else
161 Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
162 TraceLength = __gwp_asan_get_deallocation_trace(
163 AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
164 PrintBacktrace(Trace, TraceLength, Printf);
165 }
166
167 // Print the allocation trace.
168 uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
169 if (ThreadID == gwp_asan::kInvalidThreadID)
170 Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
171 else
172 Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
173 TraceLength = __gwp_asan_get_allocation_trace(
174 AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
175 PrintBacktrace(Trace, TraceLength, Printf);
176 }
177
178 struct sigaction PreviousHandler;
179 bool SignalHandlerInstalled;
180 bool RecoverableSignal;
181 gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
182 Printf_t PrintfForSignalHandler;
183 PrintBacktrace_t PrintBacktraceForSignalHandler;
184 SegvBacktrace_t BacktraceForSignalHandler;
185
sigSegvHandler(int sig,siginfo_t * info,void * ucontext)186 static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
187 const gwp_asan::AllocatorState *State =
188 GPAForSignalHandler->getAllocatorState();
189 void *FaultAddr = info->si_addr;
190 uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr);
191
192 if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) {
193 GPAForSignalHandler->preCrashReport(FaultAddr);
194
195 dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(),
196 BacktraceForSignalHandler, PrintfForSignalHandler,
197 PrintBacktraceForSignalHandler, ucontext);
198
199 if (RecoverableSignal) {
200 GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr);
201 return;
202 }
203 }
204
205 // Process any previous handlers as long as the crash wasn't a GWP-ASan crash
206 // in recoverable mode.
207 if (PreviousHandler.sa_flags & SA_SIGINFO) {
208 PreviousHandler.sa_sigaction(sig, info, ucontext);
209 } else if (PreviousHandler.sa_handler == SIG_DFL) {
210 // If the previous handler was the default handler, cause a core dump.
211 signal(SIGSEGV, SIG_DFL);
212 raise(SIGSEGV);
213 } else if (PreviousHandler.sa_handler == SIG_IGN) {
214 // If the previous segv handler was SIGIGN, crash iff we were responsible
215 // for the crash.
216 if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
217 reinterpret_cast<uintptr_t>(info->si_addr))) {
218 signal(SIGSEGV, SIG_DFL);
219 raise(SIGSEGV);
220 }
221 } else {
222 PreviousHandler.sa_handler(sig);
223 }
224 }
225 } // anonymous namespace
226
227 namespace gwp_asan {
228 namespace segv_handler {
229
installSignalHandlers(gwp_asan::GuardedPoolAllocator * GPA,Printf_t Printf,PrintBacktrace_t PrintBacktrace,SegvBacktrace_t SegvBacktrace,bool Recoverable)230 void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
231 PrintBacktrace_t PrintBacktrace,
232 SegvBacktrace_t SegvBacktrace, bool Recoverable) {
233 assert(GPA && "GPA wasn't provided to installSignalHandlers.");
234 assert(Printf && "Printf wasn't provided to installSignalHandlers.");
235 assert(PrintBacktrace &&
236 "PrintBacktrace wasn't provided to installSignalHandlers.");
237 assert(SegvBacktrace &&
238 "SegvBacktrace wasn't provided to installSignalHandlers.");
239 GPAForSignalHandler = GPA;
240 PrintfForSignalHandler = Printf;
241 PrintBacktraceForSignalHandler = PrintBacktrace;
242 BacktraceForSignalHandler = SegvBacktrace;
243 RecoverableSignal = Recoverable;
244
245 struct sigaction Action = {};
246 Action.sa_sigaction = sigSegvHandler;
247 Action.sa_flags = SA_SIGINFO;
248 sigaction(SIGSEGV, &Action, &PreviousHandler);
249 SignalHandlerInstalled = true;
250 }
251
uninstallSignalHandlers()252 void uninstallSignalHandlers() {
253 if (SignalHandlerInstalled) {
254 sigaction(SIGSEGV, &PreviousHandler, nullptr);
255 SignalHandlerInstalled = false;
256 }
257 }
258 } // namespace segv_handler
259 } // namespace gwp_asan
260