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