1ae9d0400SKostya Kortchinsky //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
2a6258684SMitch Phillips //
3a6258684SMitch Phillips // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a6258684SMitch Phillips // See https://llvm.org/LICENSE.txt for license information.
5a6258684SMitch Phillips // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a6258684SMitch Phillips //
7a6258684SMitch Phillips //===----------------------------------------------------------------------===//
8a6258684SMitch Phillips
9a6258684SMitch Phillips #include "gwp_asan/common.h"
10a6258684SMitch Phillips #include "gwp_asan/crash_handler.h"
11a6258684SMitch Phillips #include "gwp_asan/guarded_pool_allocator.h"
12a6258684SMitch Phillips #include "gwp_asan/optional/segv_handler.h"
13a6258684SMitch Phillips #include "gwp_asan/options.h"
14a6258684SMitch Phillips
15a8520f69SMitch Phillips // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
16a8520f69SMitch Phillips // macro is defined before including <inttypes.h>.
17a8520f69SMitch Phillips #ifndef __STDC_FORMAT_MACROS
18a8520f69SMitch Phillips #define __STDC_FORMAT_MACROS 1
19a8520f69SMitch Phillips #endif
20a8520f69SMitch Phillips
21a6258684SMitch Phillips #include <assert.h>
22a6258684SMitch Phillips #include <inttypes.h>
23a6258684SMitch Phillips #include <signal.h>
24a6258684SMitch Phillips #include <stdio.h>
25a6258684SMitch Phillips
26a6258684SMitch Phillips using gwp_asan::AllocationMetadata;
27a6258684SMitch Phillips using gwp_asan::Error;
28a6258684SMitch Phillips using gwp_asan::GuardedPoolAllocator;
29a8520f69SMitch Phillips using gwp_asan::Printf_t;
30a8520f69SMitch Phillips using gwp_asan::backtrace::PrintBacktrace_t;
31a8520f69SMitch Phillips using gwp_asan::backtrace::SegvBacktrace_t;
32a6258684SMitch Phillips
33a8520f69SMitch Phillips namespace {
34a6258684SMitch Phillips
35a6258684SMitch Phillips struct ScopedEndOfReportDecorator {
ScopedEndOfReportDecorator__anonfdba920e0111::ScopedEndOfReportDecorator36a8520f69SMitch Phillips ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
~ScopedEndOfReportDecorator__anonfdba920e0111::ScopedEndOfReportDecorator37a6258684SMitch Phillips ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
38a8520f69SMitch Phillips gwp_asan::Printf_t Printf;
39a6258684SMitch Phillips };
40a6258684SMitch Phillips
41a6258684SMitch Phillips // Prints the provided error and metadata information.
printHeader(Error E,uintptr_t AccessPtr,const gwp_asan::AllocationMetadata * Metadata,Printf_t Printf)42a6258684SMitch Phillips void printHeader(Error E, uintptr_t AccessPtr,
43a6258684SMitch Phillips const gwp_asan::AllocationMetadata *Metadata,
44a6258684SMitch Phillips Printf_t Printf) {
45a6258684SMitch Phillips // Print using intermediate strings. Platforms like Android don't like when
46a6258684SMitch Phillips // you print multiple times to the same line, as there may be a newline
47a6258684SMitch Phillips // appended to a log file automatically per Printf() call.
48a6258684SMitch Phillips constexpr size_t kDescriptionBufferLen = 128;
49a6258684SMitch Phillips char DescriptionBuffer[kDescriptionBufferLen] = "";
50dcf23e13SMitch Phillips
51dcf23e13SMitch Phillips bool AccessWasInBounds = false;
52a6258684SMitch Phillips if (E != Error::UNKNOWN && Metadata != nullptr) {
530bfc4890SMitch Phillips uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
540bfc4890SMitch Phillips size_t Size = __gwp_asan_get_allocation_size(Metadata);
55dcf23e13SMitch Phillips if (AccessPtr < Address) {
56a6258684SMitch Phillips snprintf(DescriptionBuffer, kDescriptionBufferLen,
57a6258684SMitch Phillips "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
58a6258684SMitch Phillips Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
59a6258684SMitch Phillips Address);
60a6258684SMitch Phillips } else if (AccessPtr > Address) {
61a6258684SMitch Phillips snprintf(DescriptionBuffer, kDescriptionBufferLen,
62a6258684SMitch Phillips "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
63a6258684SMitch Phillips AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
64a6258684SMitch Phillips Address);
65dcf23e13SMitch Phillips } else if (E == Error::DOUBLE_FREE) {
66a6258684SMitch Phillips snprintf(DescriptionBuffer, kDescriptionBufferLen,
67a6258684SMitch Phillips "(a %zu-byte allocation) ", Size);
68dcf23e13SMitch Phillips } else {
69dcf23e13SMitch Phillips AccessWasInBounds = true;
70dcf23e13SMitch Phillips snprintf(DescriptionBuffer, kDescriptionBufferLen,
71dcf23e13SMitch Phillips "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
72dcf23e13SMitch Phillips AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
73dcf23e13SMitch Phillips Address);
74a6258684SMitch Phillips }
75a6258684SMitch Phillips }
76a6258684SMitch Phillips
77a6258684SMitch Phillips // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
78a6258684SMitch Phillips // a null terminator, and round to the nearest 8-byte boundary.
79a6258684SMitch Phillips uint64_t ThreadID = gwp_asan::getThreadID();
80a6258684SMitch Phillips constexpr size_t kThreadBufferLen = 24;
81a6258684SMitch Phillips char ThreadBuffer[kThreadBufferLen];
82a6258684SMitch Phillips if (ThreadID == gwp_asan::kInvalidThreadID)
83a6258684SMitch Phillips snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
84a6258684SMitch Phillips else
85a6258684SMitch Phillips snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
86a6258684SMitch Phillips
87dcf23e13SMitch Phillips const char *OutOfBoundsAndUseAfterFreeWarning = "";
88dcf23e13SMitch Phillips if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {
89dcf23e13SMitch Phillips OutOfBoundsAndUseAfterFreeWarning =
90dcf23e13SMitch Phillips " (warning: buffer overflow/underflow detected on a free()'d "
91dcf23e13SMitch Phillips "allocation. This either means you have a buffer-overflow and a "
92dcf23e13SMitch Phillips "use-after-free at the same time, or you have a long-lived "
93dcf23e13SMitch Phillips "use-after-free bug where the allocation/deallocation metadata below "
94dcf23e13SMitch Phillips "has already been overwritten and is likely bogus)";
95dcf23e13SMitch Phillips }
96dcf23e13SMitch Phillips
97dcf23e13SMitch Phillips Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
98dcf23e13SMitch Phillips OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,
99dcf23e13SMitch Phillips ThreadBuffer);
100a6258684SMitch Phillips }
101a6258684SMitch Phillips
102bc949f92SMitch Phillips static bool HasReportedBadPoolAccess = false;
103bc949f92SMitch Phillips static const char *kUnknownCrashText =
104bc949f92SMitch Phillips "GWP-ASan cannot provide any more information about this error. This may "
105bc949f92SMitch Phillips "occur due to a wild memory access into the GWP-ASan pool, or an "
106bc949f92SMitch Phillips "overflow/underflow that is > 512B in length.\n";
107bc949f92SMitch Phillips
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)108a6258684SMitch Phillips void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
109a6258684SMitch Phillips const gwp_asan::AllocationMetadata *Metadata,
1104f029d1bSMitch Phillips SegvBacktrace_t SegvBacktrace, Printf_t Printf,
1114f029d1bSMitch Phillips PrintBacktrace_t PrintBacktrace, void *Context) {
112a6258684SMitch Phillips assert(State && "dumpReport missing Allocator State.");
113a6258684SMitch Phillips assert(Metadata && "dumpReport missing Metadata.");
114a6258684SMitch Phillips assert(Printf && "dumpReport missing Printf.");
11535b5499dSMitch Phillips assert(__gwp_asan_error_is_mine(State, ErrorPtr) &&
11635b5499dSMitch Phillips "dumpReport() called on a non-GWP-ASan error.");
117a6258684SMitch Phillips
11835b5499dSMitch Phillips uintptr_t InternalErrorPtr =
11935b5499dSMitch Phillips __gwp_asan_get_internal_crash_address(State, ErrorPtr);
12035b5499dSMitch Phillips if (InternalErrorPtr)
12135b5499dSMitch Phillips ErrorPtr = InternalErrorPtr;
12235b5499dSMitch Phillips
12335b5499dSMitch Phillips const gwp_asan::AllocationMetadata *AllocMeta =
12435b5499dSMitch Phillips __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
12535b5499dSMitch Phillips
126bc949f92SMitch Phillips if (AllocMeta == nullptr) {
127bc949f92SMitch Phillips if (HasReportedBadPoolAccess) return;
128bc949f92SMitch Phillips HasReportedBadPoolAccess = true;
129bc949f92SMitch Phillips Printf("*** GWP-ASan detected a memory error ***\n");
130bc949f92SMitch Phillips ScopedEndOfReportDecorator Decorator(Printf);
131bc949f92SMitch Phillips Printf(kUnknownCrashText);
132bc949f92SMitch Phillips return;
133bc949f92SMitch Phillips }
134bc949f92SMitch Phillips
13535b5499dSMitch Phillips // It's unusual for a signal handler to be invoked multiple times for the same
13635b5499dSMitch Phillips // allocation, but it's possible in various scenarios, like:
13735b5499dSMitch Phillips // 1. A double-free or invalid-free was invoked in one thread at the same
13835b5499dSMitch Phillips // time as a buffer-overflow or use-after-free in another thread, or
13935b5499dSMitch Phillips // 2. Two threads do a use-after-free or buffer-overflow at the same time.
14035b5499dSMitch Phillips // In these instances, we've already dumped a report for this allocation, so
14135b5499dSMitch Phillips // skip dumping this issue as well.
14235b5499dSMitch Phillips if (AllocMeta->HasCrashed)
143a6258684SMitch Phillips return;
144a6258684SMitch Phillips
145a6258684SMitch Phillips Printf("*** GWP-ASan detected a memory error ***\n");
146a6258684SMitch Phillips ScopedEndOfReportDecorator Decorator(Printf);
147a6258684SMitch Phillips
148a6258684SMitch Phillips Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
149a6258684SMitch Phillips if (E == Error::UNKNOWN) {
150bc949f92SMitch Phillips Printf(kUnknownCrashText);
151a6258684SMitch Phillips return;
152a6258684SMitch Phillips }
153a6258684SMitch Phillips
154a6258684SMitch Phillips // Print the error header.
1550bfc4890SMitch Phillips printHeader(E, ErrorPtr, AllocMeta, Printf);
156a6258684SMitch Phillips
157a6258684SMitch Phillips // Print the fault backtrace.
158a6258684SMitch Phillips static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
159a6258684SMitch Phillips uintptr_t Trace[kMaximumStackFramesForCrashTrace];
1604f029d1bSMitch Phillips size_t TraceLength =
1614f029d1bSMitch Phillips SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
162a6258684SMitch Phillips
163a6258684SMitch Phillips PrintBacktrace(Trace, TraceLength, Printf);
164a6258684SMitch Phillips
165a6258684SMitch Phillips // Maybe print the deallocation trace.
1660bfc4890SMitch Phillips if (__gwp_asan_is_deallocated(AllocMeta)) {
1670bfc4890SMitch Phillips uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
168a8520f69SMitch Phillips if (ThreadID == gwp_asan::kInvalidThreadID)
169a6258684SMitch Phillips Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
170a6258684SMitch Phillips else
171a6258684SMitch Phillips Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
172a6258684SMitch Phillips TraceLength = __gwp_asan_get_deallocation_trace(
1730bfc4890SMitch Phillips AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
174a6258684SMitch Phillips PrintBacktrace(Trace, TraceLength, Printf);
175a6258684SMitch Phillips }
176a6258684SMitch Phillips
177a6258684SMitch Phillips // Print the allocation trace.
1780bfc4890SMitch Phillips uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
179a8520f69SMitch Phillips if (ThreadID == gwp_asan::kInvalidThreadID)
180a6258684SMitch Phillips Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
181a6258684SMitch Phillips else
182a6258684SMitch Phillips Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
183a6258684SMitch Phillips TraceLength = __gwp_asan_get_allocation_trace(
1840bfc4890SMitch Phillips AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
185a6258684SMitch Phillips PrintBacktrace(Trace, TraceLength, Printf);
186a6258684SMitch Phillips }
187a8520f69SMitch Phillips
188a8520f69SMitch Phillips struct sigaction PreviousHandler;
189a8520f69SMitch Phillips bool SignalHandlerInstalled;
19035b5499dSMitch Phillips bool RecoverableSignal;
191a8520f69SMitch Phillips gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
192a8520f69SMitch Phillips Printf_t PrintfForSignalHandler;
193a8520f69SMitch Phillips PrintBacktrace_t PrintBacktraceForSignalHandler;
194a8520f69SMitch Phillips SegvBacktrace_t BacktraceForSignalHandler;
195a8520f69SMitch Phillips
sigSegvHandler(int sig,siginfo_t * info,void * ucontext)196a8520f69SMitch Phillips static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
19735b5499dSMitch Phillips const gwp_asan::AllocatorState *State =
19835b5499dSMitch Phillips GPAForSignalHandler->getAllocatorState();
19935b5499dSMitch Phillips void *FaultAddr = info->si_addr;
20035b5499dSMitch Phillips uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr);
201a8520f69SMitch Phillips
20235b5499dSMitch Phillips if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) {
20335b5499dSMitch Phillips GPAForSignalHandler->preCrashReport(FaultAddr);
20435b5499dSMitch Phillips
20535b5499dSMitch Phillips dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(),
206a8520f69SMitch Phillips BacktraceForSignalHandler, PrintfForSignalHandler,
207a8520f69SMitch Phillips PrintBacktraceForSignalHandler, ucontext);
20835b5499dSMitch Phillips
20935b5499dSMitch Phillips if (RecoverableSignal) {
21035b5499dSMitch Phillips GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr);
21135b5499dSMitch Phillips return;
21235b5499dSMitch Phillips }
213a8520f69SMitch Phillips }
214a8520f69SMitch Phillips
21535b5499dSMitch Phillips // Process any previous handlers as long as the crash wasn't a GWP-ASan crash
21635b5499dSMitch Phillips // in recoverable mode.
217a8520f69SMitch Phillips if (PreviousHandler.sa_flags & SA_SIGINFO) {
218a8520f69SMitch Phillips PreviousHandler.sa_sigaction(sig, info, ucontext);
219a8520f69SMitch Phillips } else if (PreviousHandler.sa_handler == SIG_DFL) {
220a8520f69SMitch Phillips // If the previous handler was the default handler, cause a core dump.
221a8520f69SMitch Phillips signal(SIGSEGV, SIG_DFL);
222a8520f69SMitch Phillips raise(SIGSEGV);
223a8520f69SMitch Phillips } else if (PreviousHandler.sa_handler == SIG_IGN) {
224a8520f69SMitch Phillips // If the previous segv handler was SIGIGN, crash iff we were responsible
225a8520f69SMitch Phillips // for the crash.
226a8520f69SMitch Phillips if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
227a8520f69SMitch Phillips reinterpret_cast<uintptr_t>(info->si_addr))) {
228a8520f69SMitch Phillips signal(SIGSEGV, SIG_DFL);
229a8520f69SMitch Phillips raise(SIGSEGV);
230a8520f69SMitch Phillips }
231a8520f69SMitch Phillips } else {
232a8520f69SMitch Phillips PreviousHandler.sa_handler(sig);
233a8520f69SMitch Phillips }
234a8520f69SMitch Phillips }
235a8520f69SMitch Phillips } // anonymous namespace
236a8520f69SMitch Phillips
237a8520f69SMitch Phillips namespace gwp_asan {
238a8520f69SMitch Phillips namespace segv_handler {
239a8520f69SMitch Phillips
installSignalHandlers(gwp_asan::GuardedPoolAllocator * GPA,Printf_t Printf,PrintBacktrace_t PrintBacktrace,SegvBacktrace_t SegvBacktrace,bool Recoverable)240a8520f69SMitch Phillips void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
241a8520f69SMitch Phillips PrintBacktrace_t PrintBacktrace,
24235b5499dSMitch Phillips SegvBacktrace_t SegvBacktrace, bool Recoverable) {
243a8520f69SMitch Phillips assert(GPA && "GPA wasn't provided to installSignalHandlers.");
244a8520f69SMitch Phillips assert(Printf && "Printf wasn't provided to installSignalHandlers.");
245a8520f69SMitch Phillips assert(PrintBacktrace &&
246a8520f69SMitch Phillips "PrintBacktrace wasn't provided to installSignalHandlers.");
247a8520f69SMitch Phillips assert(SegvBacktrace &&
248a8520f69SMitch Phillips "SegvBacktrace wasn't provided to installSignalHandlers.");
249a8520f69SMitch Phillips GPAForSignalHandler = GPA;
250a8520f69SMitch Phillips PrintfForSignalHandler = Printf;
251a8520f69SMitch Phillips PrintBacktraceForSignalHandler = PrintBacktrace;
252a8520f69SMitch Phillips BacktraceForSignalHandler = SegvBacktrace;
25335b5499dSMitch Phillips RecoverableSignal = Recoverable;
254a8520f69SMitch Phillips
255a8520f69SMitch Phillips struct sigaction Action = {};
256a8520f69SMitch Phillips Action.sa_sigaction = sigSegvHandler;
257a8520f69SMitch Phillips Action.sa_flags = SA_SIGINFO;
258a8520f69SMitch Phillips sigaction(SIGSEGV, &Action, &PreviousHandler);
259a8520f69SMitch Phillips SignalHandlerInstalled = true;
260*7adb7aa4SMitch Phillips HasReportedBadPoolAccess = false;
261a8520f69SMitch Phillips }
262a8520f69SMitch Phillips
uninstallSignalHandlers()263a8520f69SMitch Phillips void uninstallSignalHandlers() {
264a8520f69SMitch Phillips if (SignalHandlerInstalled) {
265a8520f69SMitch Phillips sigaction(SIGSEGV, &PreviousHandler, nullptr);
266a8520f69SMitch Phillips SignalHandlerInstalled = false;
267a8520f69SMitch Phillips }
268a8520f69SMitch Phillips }
269a8520f69SMitch Phillips } // namespace segv_handler
270a6258684SMitch Phillips } // namespace gwp_asan
271