xref: /llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp (revision 7adb7aa494247f2492f6207289ad90cb48807517)
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