xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/gwp_asan/optional/segv_handler_posix.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1e8d8bef9SDimitry Andric //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "gwp_asan/common.h"
105ffd83dbSDimitry Andric #include "gwp_asan/crash_handler.h"
115ffd83dbSDimitry Andric #include "gwp_asan/guarded_pool_allocator.h"
125ffd83dbSDimitry Andric #include "gwp_asan/optional/segv_handler.h"
135ffd83dbSDimitry Andric #include "gwp_asan/options.h"
145ffd83dbSDimitry Andric 
15e8d8bef9SDimitry Andric // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
16e8d8bef9SDimitry Andric // macro is defined before including <inttypes.h>.
17e8d8bef9SDimitry Andric #ifndef __STDC_FORMAT_MACROS
18e8d8bef9SDimitry Andric #define __STDC_FORMAT_MACROS 1
19e8d8bef9SDimitry Andric #endif
20e8d8bef9SDimitry Andric 
215ffd83dbSDimitry Andric #include <assert.h>
225ffd83dbSDimitry Andric #include <inttypes.h>
235ffd83dbSDimitry Andric #include <signal.h>
245ffd83dbSDimitry Andric #include <stdio.h>
255ffd83dbSDimitry Andric 
265ffd83dbSDimitry Andric using gwp_asan::AllocationMetadata;
275ffd83dbSDimitry Andric using gwp_asan::Error;
285ffd83dbSDimitry Andric using gwp_asan::GuardedPoolAllocator;
29e8d8bef9SDimitry Andric using gwp_asan::Printf_t;
30e8d8bef9SDimitry Andric using gwp_asan::backtrace::PrintBacktrace_t;
31e8d8bef9SDimitry Andric using gwp_asan::backtrace::SegvBacktrace_t;
325ffd83dbSDimitry Andric 
33e8d8bef9SDimitry Andric namespace {
345ffd83dbSDimitry Andric 
355ffd83dbSDimitry Andric struct ScopedEndOfReportDecorator {
36e8d8bef9SDimitry Andric   ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
375ffd83dbSDimitry Andric   ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
38e8d8bef9SDimitry Andric   gwp_asan::Printf_t Printf;
395ffd83dbSDimitry Andric };
405ffd83dbSDimitry Andric 
415ffd83dbSDimitry Andric // Prints the provided error and metadata information.
425ffd83dbSDimitry Andric void printHeader(Error E, uintptr_t AccessPtr,
435ffd83dbSDimitry Andric                  const gwp_asan::AllocationMetadata *Metadata,
445ffd83dbSDimitry Andric                  Printf_t Printf) {
455ffd83dbSDimitry Andric   // Print using intermediate strings. Platforms like Android don't like when
465ffd83dbSDimitry Andric   // you print multiple times to the same line, as there may be a newline
475ffd83dbSDimitry Andric   // appended to a log file automatically per Printf() call.
485ffd83dbSDimitry Andric   constexpr size_t kDescriptionBufferLen = 128;
495ffd83dbSDimitry Andric   char DescriptionBuffer[kDescriptionBufferLen] = "";
50bdd1243dSDimitry Andric 
51bdd1243dSDimitry Andric   bool AccessWasInBounds = false;
525ffd83dbSDimitry Andric   if (E != Error::UNKNOWN && Metadata != nullptr) {
535ffd83dbSDimitry Andric     uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
545ffd83dbSDimitry Andric     size_t Size = __gwp_asan_get_allocation_size(Metadata);
55bdd1243dSDimitry Andric     if (AccessPtr < Address) {
565ffd83dbSDimitry Andric       snprintf(DescriptionBuffer, kDescriptionBufferLen,
575ffd83dbSDimitry Andric                "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
585ffd83dbSDimitry Andric                Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
595ffd83dbSDimitry Andric                Address);
605ffd83dbSDimitry Andric     } else if (AccessPtr > Address) {
615ffd83dbSDimitry Andric       snprintf(DescriptionBuffer, kDescriptionBufferLen,
625ffd83dbSDimitry Andric                "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
635ffd83dbSDimitry Andric                AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
645ffd83dbSDimitry Andric                Address);
65bdd1243dSDimitry Andric     } else if (E == Error::DOUBLE_FREE) {
665ffd83dbSDimitry Andric       snprintf(DescriptionBuffer, kDescriptionBufferLen,
675ffd83dbSDimitry Andric                "(a %zu-byte allocation) ", Size);
68bdd1243dSDimitry Andric     } else {
69bdd1243dSDimitry Andric       AccessWasInBounds = true;
70bdd1243dSDimitry Andric       snprintf(DescriptionBuffer, kDescriptionBufferLen,
71bdd1243dSDimitry Andric                "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
72bdd1243dSDimitry Andric                AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
73bdd1243dSDimitry Andric                Address);
745ffd83dbSDimitry Andric     }
755ffd83dbSDimitry Andric   }
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric   // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
785ffd83dbSDimitry Andric   // a null terminator, and round to the nearest 8-byte boundary.
795ffd83dbSDimitry Andric   uint64_t ThreadID = gwp_asan::getThreadID();
805ffd83dbSDimitry Andric   constexpr size_t kThreadBufferLen = 24;
815ffd83dbSDimitry Andric   char ThreadBuffer[kThreadBufferLen];
825ffd83dbSDimitry Andric   if (ThreadID == gwp_asan::kInvalidThreadID)
835ffd83dbSDimitry Andric     snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
845ffd83dbSDimitry Andric   else
855ffd83dbSDimitry Andric     snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
865ffd83dbSDimitry Andric 
87bdd1243dSDimitry Andric   const char *OutOfBoundsAndUseAfterFreeWarning = "";
88bdd1243dSDimitry Andric   if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {
89bdd1243dSDimitry Andric     OutOfBoundsAndUseAfterFreeWarning =
90bdd1243dSDimitry Andric         " (warning: buffer overflow/underflow detected on a free()'d "
91bdd1243dSDimitry Andric         "allocation. This either means you have a buffer-overflow and a "
92bdd1243dSDimitry Andric         "use-after-free at the same time, or you have a long-lived "
93bdd1243dSDimitry Andric         "use-after-free bug where the allocation/deallocation metadata below "
94bdd1243dSDimitry Andric         "has already been overwritten and is likely bogus)";
95bdd1243dSDimitry Andric   }
96bdd1243dSDimitry Andric 
97bdd1243dSDimitry Andric   Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
98bdd1243dSDimitry Andric          OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,
99bdd1243dSDimitry Andric          ThreadBuffer);
1005ffd83dbSDimitry Andric }
1015ffd83dbSDimitry Andric 
10206c3fb27SDimitry Andric static bool HasReportedBadPoolAccess = false;
10306c3fb27SDimitry Andric static const char *kUnknownCrashText =
10406c3fb27SDimitry Andric     "GWP-ASan cannot provide any more information about this error. This may "
10506c3fb27SDimitry Andric     "occur due to a wild memory access into the GWP-ASan pool, or an "
10606c3fb27SDimitry Andric     "overflow/underflow that is > 512B in length.\n";
10706c3fb27SDimitry Andric 
1085ffd83dbSDimitry Andric void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
1095ffd83dbSDimitry Andric                 const gwp_asan::AllocationMetadata *Metadata,
110e8d8bef9SDimitry Andric                 SegvBacktrace_t SegvBacktrace, Printf_t Printf,
111e8d8bef9SDimitry Andric                 PrintBacktrace_t PrintBacktrace, void *Context) {
1125ffd83dbSDimitry Andric   assert(State && "dumpReport missing Allocator State.");
1135ffd83dbSDimitry Andric   assert(Metadata && "dumpReport missing Metadata.");
1145ffd83dbSDimitry Andric   assert(Printf && "dumpReport missing Printf.");
115bdd1243dSDimitry Andric   assert(__gwp_asan_error_is_mine(State, ErrorPtr) &&
116bdd1243dSDimitry Andric          "dumpReport() called on a non-GWP-ASan error.");
1175ffd83dbSDimitry Andric 
118bdd1243dSDimitry Andric   uintptr_t InternalErrorPtr =
119bdd1243dSDimitry Andric       __gwp_asan_get_internal_crash_address(State, ErrorPtr);
120bdd1243dSDimitry Andric   if (InternalErrorPtr)
121bdd1243dSDimitry Andric     ErrorPtr = InternalErrorPtr;
122bdd1243dSDimitry Andric 
123bdd1243dSDimitry Andric   const gwp_asan::AllocationMetadata *AllocMeta =
124bdd1243dSDimitry Andric       __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
125bdd1243dSDimitry Andric 
12606c3fb27SDimitry Andric   if (AllocMeta == nullptr) {
12706c3fb27SDimitry Andric     if (HasReportedBadPoolAccess) return;
12806c3fb27SDimitry Andric     HasReportedBadPoolAccess = true;
12906c3fb27SDimitry Andric     Printf("*** GWP-ASan detected a memory error ***\n");
13006c3fb27SDimitry Andric     ScopedEndOfReportDecorator Decorator(Printf);
13106c3fb27SDimitry Andric     Printf(kUnknownCrashText);
13206c3fb27SDimitry Andric     return;
13306c3fb27SDimitry Andric   }
13406c3fb27SDimitry Andric 
135bdd1243dSDimitry Andric   // It's unusual for a signal handler to be invoked multiple times for the same
136bdd1243dSDimitry Andric   // allocation, but it's possible in various scenarios, like:
137bdd1243dSDimitry Andric   //  1. A double-free or invalid-free was invoked in one thread at the same
138bdd1243dSDimitry Andric   //     time as a buffer-overflow or use-after-free in another thread, or
139bdd1243dSDimitry Andric   //  2. Two threads do a use-after-free or buffer-overflow at the same time.
140bdd1243dSDimitry Andric   // In these instances, we've already dumped a report for this allocation, so
141bdd1243dSDimitry Andric   // skip dumping this issue as well.
142bdd1243dSDimitry Andric   if (AllocMeta->HasCrashed)
1435ffd83dbSDimitry Andric     return;
1445ffd83dbSDimitry Andric 
1455ffd83dbSDimitry Andric   Printf("*** GWP-ASan detected a memory error ***\n");
1465ffd83dbSDimitry Andric   ScopedEndOfReportDecorator Decorator(Printf);
1475ffd83dbSDimitry Andric 
1485ffd83dbSDimitry Andric   Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
1495ffd83dbSDimitry Andric   if (E == Error::UNKNOWN) {
15006c3fb27SDimitry Andric     Printf(kUnknownCrashText);
1515ffd83dbSDimitry Andric     return;
1525ffd83dbSDimitry Andric   }
1535ffd83dbSDimitry Andric 
1545ffd83dbSDimitry Andric   // Print the error header.
1555ffd83dbSDimitry Andric   printHeader(E, ErrorPtr, AllocMeta, Printf);
1565ffd83dbSDimitry Andric 
1575ffd83dbSDimitry Andric   // Print the fault backtrace.
1585ffd83dbSDimitry Andric   static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
1595ffd83dbSDimitry Andric   uintptr_t Trace[kMaximumStackFramesForCrashTrace];
160e8d8bef9SDimitry Andric   size_t TraceLength =
161e8d8bef9SDimitry Andric       SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
1625ffd83dbSDimitry Andric 
1635ffd83dbSDimitry Andric   PrintBacktrace(Trace, TraceLength, Printf);
1645ffd83dbSDimitry Andric 
1655ffd83dbSDimitry Andric   // Maybe print the deallocation trace.
1665ffd83dbSDimitry Andric   if (__gwp_asan_is_deallocated(AllocMeta)) {
1675ffd83dbSDimitry Andric     uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
168e8d8bef9SDimitry Andric     if (ThreadID == gwp_asan::kInvalidThreadID)
1695ffd83dbSDimitry Andric       Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
1705ffd83dbSDimitry Andric     else
1715ffd83dbSDimitry Andric       Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
1725ffd83dbSDimitry Andric     TraceLength = __gwp_asan_get_deallocation_trace(
1735ffd83dbSDimitry Andric         AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
1745ffd83dbSDimitry Andric     PrintBacktrace(Trace, TraceLength, Printf);
1755ffd83dbSDimitry Andric   }
1765ffd83dbSDimitry Andric 
1775ffd83dbSDimitry Andric   // Print the allocation trace.
1785ffd83dbSDimitry Andric   uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
179e8d8bef9SDimitry Andric   if (ThreadID == gwp_asan::kInvalidThreadID)
1805ffd83dbSDimitry Andric     Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
1815ffd83dbSDimitry Andric   else
1825ffd83dbSDimitry Andric     Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
1835ffd83dbSDimitry Andric   TraceLength = __gwp_asan_get_allocation_trace(
1845ffd83dbSDimitry Andric       AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
1855ffd83dbSDimitry Andric   PrintBacktrace(Trace, TraceLength, Printf);
1865ffd83dbSDimitry Andric }
187e8d8bef9SDimitry Andric 
188e8d8bef9SDimitry Andric struct sigaction PreviousHandler;
189e8d8bef9SDimitry Andric bool SignalHandlerInstalled;
190bdd1243dSDimitry Andric bool RecoverableSignal;
191e8d8bef9SDimitry Andric gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
192e8d8bef9SDimitry Andric Printf_t PrintfForSignalHandler;
193e8d8bef9SDimitry Andric PrintBacktrace_t PrintBacktraceForSignalHandler;
194e8d8bef9SDimitry Andric SegvBacktrace_t BacktraceForSignalHandler;
195e8d8bef9SDimitry Andric 
196e8d8bef9SDimitry Andric static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
197bdd1243dSDimitry Andric   const gwp_asan::AllocatorState *State =
198bdd1243dSDimitry Andric       GPAForSignalHandler->getAllocatorState();
199bdd1243dSDimitry Andric   void *FaultAddr = info->si_addr;
200bdd1243dSDimitry Andric   uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr);
201e8d8bef9SDimitry Andric 
202bdd1243dSDimitry Andric   if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) {
203bdd1243dSDimitry Andric     GPAForSignalHandler->preCrashReport(FaultAddr);
204bdd1243dSDimitry Andric 
205bdd1243dSDimitry Andric     dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(),
206e8d8bef9SDimitry Andric                BacktraceForSignalHandler, PrintfForSignalHandler,
207e8d8bef9SDimitry Andric                PrintBacktraceForSignalHandler, ucontext);
208bdd1243dSDimitry Andric 
209bdd1243dSDimitry Andric     if (RecoverableSignal) {
210bdd1243dSDimitry Andric       GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr);
211bdd1243dSDimitry Andric       return;
212bdd1243dSDimitry Andric     }
213e8d8bef9SDimitry Andric   }
214e8d8bef9SDimitry Andric 
215bdd1243dSDimitry Andric   // Process any previous handlers as long as the crash wasn't a GWP-ASan crash
216bdd1243dSDimitry Andric   // in recoverable mode.
217e8d8bef9SDimitry Andric   if (PreviousHandler.sa_flags & SA_SIGINFO) {
218e8d8bef9SDimitry Andric     PreviousHandler.sa_sigaction(sig, info, ucontext);
219e8d8bef9SDimitry Andric   } else if (PreviousHandler.sa_handler == SIG_DFL) {
220e8d8bef9SDimitry Andric     // If the previous handler was the default handler, cause a core dump.
221e8d8bef9SDimitry Andric     signal(SIGSEGV, SIG_DFL);
222e8d8bef9SDimitry Andric     raise(SIGSEGV);
223e8d8bef9SDimitry Andric   } else if (PreviousHandler.sa_handler == SIG_IGN) {
224e8d8bef9SDimitry Andric     // If the previous segv handler was SIGIGN, crash iff we were responsible
225e8d8bef9SDimitry Andric     // for the crash.
226e8d8bef9SDimitry Andric     if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
227e8d8bef9SDimitry Andric                                  reinterpret_cast<uintptr_t>(info->si_addr))) {
228e8d8bef9SDimitry Andric       signal(SIGSEGV, SIG_DFL);
229e8d8bef9SDimitry Andric       raise(SIGSEGV);
230e8d8bef9SDimitry Andric     }
231e8d8bef9SDimitry Andric   } else {
232e8d8bef9SDimitry Andric     PreviousHandler.sa_handler(sig);
233e8d8bef9SDimitry Andric   }
234e8d8bef9SDimitry Andric }
235e8d8bef9SDimitry Andric } // anonymous namespace
236e8d8bef9SDimitry Andric 
237e8d8bef9SDimitry Andric namespace gwp_asan {
238e8d8bef9SDimitry Andric namespace segv_handler {
239e8d8bef9SDimitry Andric 
240e8d8bef9SDimitry Andric void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
241e8d8bef9SDimitry Andric                            PrintBacktrace_t PrintBacktrace,
242bdd1243dSDimitry Andric                            SegvBacktrace_t SegvBacktrace, bool Recoverable) {
243e8d8bef9SDimitry Andric   assert(GPA && "GPA wasn't provided to installSignalHandlers.");
244e8d8bef9SDimitry Andric   assert(Printf && "Printf wasn't provided to installSignalHandlers.");
245e8d8bef9SDimitry Andric   assert(PrintBacktrace &&
246e8d8bef9SDimitry Andric          "PrintBacktrace wasn't provided to installSignalHandlers.");
247e8d8bef9SDimitry Andric   assert(SegvBacktrace &&
248e8d8bef9SDimitry Andric          "SegvBacktrace wasn't provided to installSignalHandlers.");
249e8d8bef9SDimitry Andric   GPAForSignalHandler = GPA;
250e8d8bef9SDimitry Andric   PrintfForSignalHandler = Printf;
251e8d8bef9SDimitry Andric   PrintBacktraceForSignalHandler = PrintBacktrace;
252e8d8bef9SDimitry Andric   BacktraceForSignalHandler = SegvBacktrace;
253bdd1243dSDimitry Andric   RecoverableSignal = Recoverable;
254e8d8bef9SDimitry Andric 
255e8d8bef9SDimitry Andric   struct sigaction Action = {};
256e8d8bef9SDimitry Andric   Action.sa_sigaction = sigSegvHandler;
257e8d8bef9SDimitry Andric   Action.sa_flags = SA_SIGINFO;
258e8d8bef9SDimitry Andric   sigaction(SIGSEGV, &Action, &PreviousHandler);
259e8d8bef9SDimitry Andric   SignalHandlerInstalled = true;
260*0fca6ea1SDimitry Andric   HasReportedBadPoolAccess = false;
261e8d8bef9SDimitry Andric }
262e8d8bef9SDimitry Andric 
263e8d8bef9SDimitry Andric void uninstallSignalHandlers() {
264e8d8bef9SDimitry Andric   if (SignalHandlerInstalled) {
265e8d8bef9SDimitry Andric     sigaction(SIGSEGV, &PreviousHandler, nullptr);
266e8d8bef9SDimitry Andric     SignalHandlerInstalled = false;
267e8d8bef9SDimitry Andric   }
268e8d8bef9SDimitry Andric }
269e8d8bef9SDimitry Andric } // namespace segv_handler
2705ffd83dbSDimitry Andric } // namespace gwp_asan
271