1d89ec533Spatrick //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
21f9cb04fSpatrick //
31f9cb04fSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41f9cb04fSpatrick // See https://llvm.org/LICENSE.txt for license information.
51f9cb04fSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61f9cb04fSpatrick //
71f9cb04fSpatrick //===----------------------------------------------------------------------===//
81f9cb04fSpatrick
91f9cb04fSpatrick #include "gwp_asan/common.h"
101f9cb04fSpatrick #include "gwp_asan/crash_handler.h"
111f9cb04fSpatrick #include "gwp_asan/guarded_pool_allocator.h"
121f9cb04fSpatrick #include "gwp_asan/optional/segv_handler.h"
131f9cb04fSpatrick #include "gwp_asan/options.h"
141f9cb04fSpatrick
15d89ec533Spatrick // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
16d89ec533Spatrick // macro is defined before including <inttypes.h>.
17d89ec533Spatrick #ifndef __STDC_FORMAT_MACROS
18d89ec533Spatrick #define __STDC_FORMAT_MACROS 1
19d89ec533Spatrick #endif
20d89ec533Spatrick
211f9cb04fSpatrick #include <assert.h>
221f9cb04fSpatrick #include <inttypes.h>
231f9cb04fSpatrick #include <signal.h>
241f9cb04fSpatrick #include <stdio.h>
251f9cb04fSpatrick
261f9cb04fSpatrick using gwp_asan::AllocationMetadata;
271f9cb04fSpatrick using gwp_asan::Error;
281f9cb04fSpatrick using gwp_asan::GuardedPoolAllocator;
29d89ec533Spatrick using gwp_asan::Printf_t;
30d89ec533Spatrick using gwp_asan::backtrace::PrintBacktrace_t;
31d89ec533Spatrick using gwp_asan::backtrace::SegvBacktrace_t;
321f9cb04fSpatrick
33d89ec533Spatrick namespace {
341f9cb04fSpatrick
351f9cb04fSpatrick struct ScopedEndOfReportDecorator {
ScopedEndOfReportDecorator__anon647644720111::ScopedEndOfReportDecorator36d89ec533Spatrick ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
~ScopedEndOfReportDecorator__anon647644720111::ScopedEndOfReportDecorator371f9cb04fSpatrick ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
38d89ec533Spatrick gwp_asan::Printf_t Printf;
391f9cb04fSpatrick };
401f9cb04fSpatrick
411f9cb04fSpatrick // Prints the provided error and metadata information.
printHeader(Error E,uintptr_t AccessPtr,const gwp_asan::AllocationMetadata * Metadata,Printf_t Printf)421f9cb04fSpatrick void printHeader(Error E, uintptr_t AccessPtr,
431f9cb04fSpatrick const gwp_asan::AllocationMetadata *Metadata,
441f9cb04fSpatrick Printf_t Printf) {
451f9cb04fSpatrick // Print using intermediate strings. Platforms like Android don't like when
461f9cb04fSpatrick // you print multiple times to the same line, as there may be a newline
471f9cb04fSpatrick // appended to a log file automatically per Printf() call.
481f9cb04fSpatrick constexpr size_t kDescriptionBufferLen = 128;
491f9cb04fSpatrick char DescriptionBuffer[kDescriptionBufferLen] = "";
50*810390e3Srobert
51*810390e3Srobert bool AccessWasInBounds = false;
521f9cb04fSpatrick if (E != Error::UNKNOWN && Metadata != nullptr) {
531f9cb04fSpatrick uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
541f9cb04fSpatrick size_t Size = __gwp_asan_get_allocation_size(Metadata);
55*810390e3Srobert if (AccessPtr < Address) {
561f9cb04fSpatrick snprintf(DescriptionBuffer, kDescriptionBufferLen,
571f9cb04fSpatrick "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
581f9cb04fSpatrick Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
591f9cb04fSpatrick Address);
601f9cb04fSpatrick } else if (AccessPtr > Address) {
611f9cb04fSpatrick snprintf(DescriptionBuffer, kDescriptionBufferLen,
621f9cb04fSpatrick "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
631f9cb04fSpatrick AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
641f9cb04fSpatrick Address);
65*810390e3Srobert } else if (E == Error::DOUBLE_FREE) {
661f9cb04fSpatrick snprintf(DescriptionBuffer, kDescriptionBufferLen,
671f9cb04fSpatrick "(a %zu-byte allocation) ", Size);
68*810390e3Srobert } else {
69*810390e3Srobert AccessWasInBounds = true;
70*810390e3Srobert snprintf(DescriptionBuffer, kDescriptionBufferLen,
71*810390e3Srobert "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
72*810390e3Srobert AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
73*810390e3Srobert Address);
741f9cb04fSpatrick }
751f9cb04fSpatrick }
761f9cb04fSpatrick
771f9cb04fSpatrick // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
781f9cb04fSpatrick // a null terminator, and round to the nearest 8-byte boundary.
791f9cb04fSpatrick uint64_t ThreadID = gwp_asan::getThreadID();
801f9cb04fSpatrick constexpr size_t kThreadBufferLen = 24;
811f9cb04fSpatrick char ThreadBuffer[kThreadBufferLen];
821f9cb04fSpatrick if (ThreadID == gwp_asan::kInvalidThreadID)
831f9cb04fSpatrick snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
841f9cb04fSpatrick else
851f9cb04fSpatrick snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
861f9cb04fSpatrick
87*810390e3Srobert const char *OutOfBoundsAndUseAfterFreeWarning = "";
88*810390e3Srobert if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {
89*810390e3Srobert OutOfBoundsAndUseAfterFreeWarning =
90*810390e3Srobert " (warning: buffer overflow/underflow detected on a free()'d "
91*810390e3Srobert "allocation. This either means you have a buffer-overflow and a "
92*810390e3Srobert "use-after-free at the same time, or you have a long-lived "
93*810390e3Srobert "use-after-free bug where the allocation/deallocation metadata below "
94*810390e3Srobert "has already been overwritten and is likely bogus)";
95*810390e3Srobert }
96*810390e3Srobert
97*810390e3Srobert Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
98*810390e3Srobert OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,
99*810390e3Srobert ThreadBuffer);
1001f9cb04fSpatrick }
1011f9cb04fSpatrick
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)1021f9cb04fSpatrick void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
1031f9cb04fSpatrick const gwp_asan::AllocationMetadata *Metadata,
104d89ec533Spatrick SegvBacktrace_t SegvBacktrace, Printf_t Printf,
105d89ec533Spatrick PrintBacktrace_t PrintBacktrace, void *Context) {
1061f9cb04fSpatrick assert(State && "dumpReport missing Allocator State.");
1071f9cb04fSpatrick assert(Metadata && "dumpReport missing Metadata.");
1081f9cb04fSpatrick assert(Printf && "dumpReport missing Printf.");
109*810390e3Srobert assert(__gwp_asan_error_is_mine(State, ErrorPtr) &&
110*810390e3Srobert "dumpReport() called on a non-GWP-ASan error.");
1111f9cb04fSpatrick
112*810390e3Srobert uintptr_t InternalErrorPtr =
113*810390e3Srobert __gwp_asan_get_internal_crash_address(State, ErrorPtr);
114*810390e3Srobert if (InternalErrorPtr)
115*810390e3Srobert ErrorPtr = InternalErrorPtr;
116*810390e3Srobert
117*810390e3Srobert const gwp_asan::AllocationMetadata *AllocMeta =
118*810390e3Srobert __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
119*810390e3Srobert
120*810390e3Srobert // It's unusual for a signal handler to be invoked multiple times for the same
121*810390e3Srobert // allocation, but it's possible in various scenarios, like:
122*810390e3Srobert // 1. A double-free or invalid-free was invoked in one thread at the same
123*810390e3Srobert // time as a buffer-overflow or use-after-free in another thread, or
124*810390e3Srobert // 2. Two threads do a use-after-free or buffer-overflow at the same time.
125*810390e3Srobert // In these instances, we've already dumped a report for this allocation, so
126*810390e3Srobert // skip dumping this issue as well.
127*810390e3Srobert if (AllocMeta->HasCrashed)
1281f9cb04fSpatrick return;
1291f9cb04fSpatrick
1301f9cb04fSpatrick Printf("*** GWP-ASan detected a memory error ***\n");
1311f9cb04fSpatrick ScopedEndOfReportDecorator Decorator(Printf);
1321f9cb04fSpatrick
1331f9cb04fSpatrick Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
1341f9cb04fSpatrick if (E == Error::UNKNOWN) {
1351f9cb04fSpatrick Printf("GWP-ASan cannot provide any more information about this error. "
1361f9cb04fSpatrick "This may occur due to a wild memory access into the GWP-ASan pool, "
1371f9cb04fSpatrick "or an overflow/underflow that is > 512B in length.\n");
1381f9cb04fSpatrick return;
1391f9cb04fSpatrick }
1401f9cb04fSpatrick
1411f9cb04fSpatrick // Print the error header.
1421f9cb04fSpatrick printHeader(E, ErrorPtr, AllocMeta, Printf);
1431f9cb04fSpatrick
1441f9cb04fSpatrick // Print the fault backtrace.
1451f9cb04fSpatrick static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
1461f9cb04fSpatrick uintptr_t Trace[kMaximumStackFramesForCrashTrace];
147d89ec533Spatrick size_t TraceLength =
148d89ec533Spatrick SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
1491f9cb04fSpatrick
1501f9cb04fSpatrick PrintBacktrace(Trace, TraceLength, Printf);
1511f9cb04fSpatrick
1521f9cb04fSpatrick if (AllocMeta == nullptr)
1531f9cb04fSpatrick return;
1541f9cb04fSpatrick
1551f9cb04fSpatrick // Maybe print the deallocation trace.
1561f9cb04fSpatrick if (__gwp_asan_is_deallocated(AllocMeta)) {
1571f9cb04fSpatrick uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
158d89ec533Spatrick if (ThreadID == gwp_asan::kInvalidThreadID)
1591f9cb04fSpatrick Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
1601f9cb04fSpatrick else
1611f9cb04fSpatrick Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
1621f9cb04fSpatrick TraceLength = __gwp_asan_get_deallocation_trace(
1631f9cb04fSpatrick AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
1641f9cb04fSpatrick PrintBacktrace(Trace, TraceLength, Printf);
1651f9cb04fSpatrick }
1661f9cb04fSpatrick
1671f9cb04fSpatrick // Print the allocation trace.
1681f9cb04fSpatrick uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
169d89ec533Spatrick if (ThreadID == gwp_asan::kInvalidThreadID)
1701f9cb04fSpatrick Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
1711f9cb04fSpatrick else
1721f9cb04fSpatrick Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
1731f9cb04fSpatrick TraceLength = __gwp_asan_get_allocation_trace(
1741f9cb04fSpatrick AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
1751f9cb04fSpatrick PrintBacktrace(Trace, TraceLength, Printf);
1761f9cb04fSpatrick }
177d89ec533Spatrick
178d89ec533Spatrick struct sigaction PreviousHandler;
179d89ec533Spatrick bool SignalHandlerInstalled;
180*810390e3Srobert bool RecoverableSignal;
181d89ec533Spatrick gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
182d89ec533Spatrick Printf_t PrintfForSignalHandler;
183d89ec533Spatrick PrintBacktrace_t PrintBacktraceForSignalHandler;
184d89ec533Spatrick SegvBacktrace_t BacktraceForSignalHandler;
185d89ec533Spatrick
sigSegvHandler(int sig,siginfo_t * info,void * ucontext)186d89ec533Spatrick static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
187*810390e3Srobert const gwp_asan::AllocatorState *State =
188*810390e3Srobert GPAForSignalHandler->getAllocatorState();
189*810390e3Srobert void *FaultAddr = info->si_addr;
190*810390e3Srobert uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr);
191d89ec533Spatrick
192*810390e3Srobert if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) {
193*810390e3Srobert GPAForSignalHandler->preCrashReport(FaultAddr);
194*810390e3Srobert
195*810390e3Srobert dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(),
196d89ec533Spatrick BacktraceForSignalHandler, PrintfForSignalHandler,
197d89ec533Spatrick PrintBacktraceForSignalHandler, ucontext);
198*810390e3Srobert
199*810390e3Srobert if (RecoverableSignal) {
200*810390e3Srobert GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr);
201*810390e3Srobert return;
202*810390e3Srobert }
203d89ec533Spatrick }
204d89ec533Spatrick
205*810390e3Srobert // Process any previous handlers as long as the crash wasn't a GWP-ASan crash
206*810390e3Srobert // in recoverable mode.
207d89ec533Spatrick if (PreviousHandler.sa_flags & SA_SIGINFO) {
208d89ec533Spatrick PreviousHandler.sa_sigaction(sig, info, ucontext);
209d89ec533Spatrick } else if (PreviousHandler.sa_handler == SIG_DFL) {
210d89ec533Spatrick // If the previous handler was the default handler, cause a core dump.
211d89ec533Spatrick signal(SIGSEGV, SIG_DFL);
212d89ec533Spatrick raise(SIGSEGV);
213d89ec533Spatrick } else if (PreviousHandler.sa_handler == SIG_IGN) {
214d89ec533Spatrick // If the previous segv handler was SIGIGN, crash iff we were responsible
215d89ec533Spatrick // for the crash.
216d89ec533Spatrick if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
217d89ec533Spatrick reinterpret_cast<uintptr_t>(info->si_addr))) {
218d89ec533Spatrick signal(SIGSEGV, SIG_DFL);
219d89ec533Spatrick raise(SIGSEGV);
220d89ec533Spatrick }
221d89ec533Spatrick } else {
222d89ec533Spatrick PreviousHandler.sa_handler(sig);
223d89ec533Spatrick }
224d89ec533Spatrick }
225d89ec533Spatrick } // anonymous namespace
226d89ec533Spatrick
227d89ec533Spatrick namespace gwp_asan {
228d89ec533Spatrick namespace segv_handler {
229d89ec533Spatrick
installSignalHandlers(gwp_asan::GuardedPoolAllocator * GPA,Printf_t Printf,PrintBacktrace_t PrintBacktrace,SegvBacktrace_t SegvBacktrace,bool Recoverable)230d89ec533Spatrick void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
231d89ec533Spatrick PrintBacktrace_t PrintBacktrace,
232*810390e3Srobert SegvBacktrace_t SegvBacktrace, bool Recoverable) {
233d89ec533Spatrick assert(GPA && "GPA wasn't provided to installSignalHandlers.");
234d89ec533Spatrick assert(Printf && "Printf wasn't provided to installSignalHandlers.");
235d89ec533Spatrick assert(PrintBacktrace &&
236d89ec533Spatrick "PrintBacktrace wasn't provided to installSignalHandlers.");
237d89ec533Spatrick assert(SegvBacktrace &&
238d89ec533Spatrick "SegvBacktrace wasn't provided to installSignalHandlers.");
239d89ec533Spatrick GPAForSignalHandler = GPA;
240d89ec533Spatrick PrintfForSignalHandler = Printf;
241d89ec533Spatrick PrintBacktraceForSignalHandler = PrintBacktrace;
242d89ec533Spatrick BacktraceForSignalHandler = SegvBacktrace;
243*810390e3Srobert RecoverableSignal = Recoverable;
244d89ec533Spatrick
245d89ec533Spatrick struct sigaction Action = {};
246d89ec533Spatrick Action.sa_sigaction = sigSegvHandler;
247d89ec533Spatrick Action.sa_flags = SA_SIGINFO;
248d89ec533Spatrick sigaction(SIGSEGV, &Action, &PreviousHandler);
249d89ec533Spatrick SignalHandlerInstalled = true;
250d89ec533Spatrick }
251d89ec533Spatrick
uninstallSignalHandlers()252d89ec533Spatrick void uninstallSignalHandlers() {
253d89ec533Spatrick if (SignalHandlerInstalled) {
254d89ec533Spatrick sigaction(SIGSEGV, &PreviousHandler, nullptr);
255d89ec533Spatrick SignalHandlerInstalled = false;
256d89ec533Spatrick }
257d89ec533Spatrick }
258d89ec533Spatrick } // namespace segv_handler
2591f9cb04fSpatrick } // namespace gwp_asan
260