brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · ae44937 Raw
271 lines · cpp
1//===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "gwp_asan/common.h"10#include "gwp_asan/crash_handler.h"11#include "gwp_asan/guarded_pool_allocator.h"12#include "gwp_asan/optional/segv_handler.h"13#include "gwp_asan/options.h"14 15// RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this16// macro is defined before including <inttypes.h>.17#ifndef __STDC_FORMAT_MACROS18#define __STDC_FORMAT_MACROS 119#endif20 21#include <assert.h>22#include <inttypes.h>23#include <signal.h>24#include <stdio.h>25 26using gwp_asan::AllocationMetadata;27using gwp_asan::Error;28using gwp_asan::GuardedPoolAllocator;29using gwp_asan::Printf_t;30using gwp_asan::backtrace::PrintBacktrace_t;31using gwp_asan::backtrace::SegvBacktrace_t;32 33namespace {34 35struct ScopedEndOfReportDecorator {36  ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}37  ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }38  gwp_asan::Printf_t Printf;39};40 41// Prints the provided error and metadata information.42void printHeader(Error E, uintptr_t AccessPtr,43                 const gwp_asan::AllocationMetadata *Metadata,44                 Printf_t Printf) {45  // Print using intermediate strings. Platforms like Android don't like when46  // you print multiple times to the same line, as there may be a newline47  // appended to a log file automatically per Printf() call.48  constexpr size_t kDescriptionBufferLen = 128;49  char DescriptionBuffer[kDescriptionBufferLen] = "";50 51  bool AccessWasInBounds = false;52  if (E != Error::UNKNOWN && Metadata != nullptr) {53    uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);54    size_t Size = __gwp_asan_get_allocation_size(Metadata);55    if (AccessPtr < Address) {56      snprintf(DescriptionBuffer, kDescriptionBufferLen,57               "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",58               Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,59               Address);60    } else if (AccessPtr > Address) {61      snprintf(DescriptionBuffer, kDescriptionBufferLen,62               "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",63               AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,64               Address);65    } else if (E == Error::DOUBLE_FREE) {66      snprintf(DescriptionBuffer, kDescriptionBufferLen,67               "(a %zu-byte allocation) ", Size);68    } else {69      AccessWasInBounds = true;70      snprintf(DescriptionBuffer, kDescriptionBufferLen,71               "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",72               AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,73               Address);74    }75  }76 77  // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add78  // a null terminator, and round to the nearest 8-byte boundary.79  uint64_t ThreadID = gwp_asan::getThreadID();80  constexpr size_t kThreadBufferLen = 24;81  char ThreadBuffer[kThreadBufferLen];82  if (ThreadID == gwp_asan::kInvalidThreadID)83    snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");84  else85    snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);86 87  const char *OutOfBoundsAndUseAfterFreeWarning = "";88  if (E == Error::USE_AFTER_FREE && !AccessWasInBounds) {89    OutOfBoundsAndUseAfterFreeWarning =90        " (warning: buffer overflow/underflow detected on a free()'d "91        "allocation. This either means you have a buffer-overflow and a "92        "use-after-free at the same time, or you have a long-lived "93        "use-after-free bug where the allocation/deallocation metadata below "94        "has already been overwritten and is likely bogus)";95  }96 97  Printf("%s%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),98         OutOfBoundsAndUseAfterFreeWarning, AccessPtr, DescriptionBuffer,99         ThreadBuffer);100}101 102static bool HasReportedBadPoolAccess = false;103static const char *kUnknownCrashText =104    "GWP-ASan cannot provide any more information about this error. This may "105    "occur due to a wild memory access into the GWP-ASan pool, or an "106    "overflow/underflow that is > 512B in length.\n";107 108void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,109                const gwp_asan::AllocationMetadata *Metadata,110                SegvBacktrace_t SegvBacktrace, Printf_t Printf,111                PrintBacktrace_t PrintBacktrace, void *Context) {112  assert(State && "dumpReport missing Allocator State.");113  assert(Metadata && "dumpReport missing Metadata.");114  assert(Printf && "dumpReport missing Printf.");115  assert(__gwp_asan_error_is_mine(State, ErrorPtr) &&116         "dumpReport() called on a non-GWP-ASan error.");117 118  uintptr_t InternalErrorPtr =119      __gwp_asan_get_internal_crash_address(State, ErrorPtr);120  if (InternalErrorPtr)121    ErrorPtr = InternalErrorPtr;122 123  const gwp_asan::AllocationMetadata *AllocMeta =124      __gwp_asan_get_metadata(State, Metadata, ErrorPtr);125 126  if (AllocMeta == nullptr) {127    if (HasReportedBadPoolAccess) return;128    HasReportedBadPoolAccess = true;129    Printf("*** GWP-ASan detected a memory error ***\n");130    ScopedEndOfReportDecorator Decorator(Printf);131    Printf(kUnknownCrashText);132    return;133  }134 135  // It's unusual for a signal handler to be invoked multiple times for the same136  // allocation, but it's possible in various scenarios, like:137  //  1. A double-free or invalid-free was invoked in one thread at the same138  //     time as a buffer-overflow or use-after-free in another thread, or139  //  2. Two threads do a use-after-free or buffer-overflow at the same time.140  // In these instances, we've already dumped a report for this allocation, so141  // skip dumping this issue as well.142  if (AllocMeta->HasCrashed)143    return;144 145  Printf("*** GWP-ASan detected a memory error ***\n");146  ScopedEndOfReportDecorator Decorator(Printf);147 148  Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);149  if (E == Error::UNKNOWN) {150    Printf(kUnknownCrashText);151    return;152  }153 154  // Print the error header.155  printHeader(E, ErrorPtr, AllocMeta, Printf);156 157  // Print the fault backtrace.158  static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;159  uintptr_t Trace[kMaximumStackFramesForCrashTrace];160  size_t TraceLength =161      SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);162 163  PrintBacktrace(Trace, TraceLength, Printf);164 165  // Maybe print the deallocation trace.166  if (__gwp_asan_is_deallocated(AllocMeta)) {167    uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);168    if (ThreadID == gwp_asan::kInvalidThreadID)169      Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);170    else171      Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);172    TraceLength = __gwp_asan_get_deallocation_trace(173        AllocMeta, Trace, kMaximumStackFramesForCrashTrace);174    PrintBacktrace(Trace, TraceLength, Printf);175  }176 177  // Print the allocation trace.178  uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);179  if (ThreadID == gwp_asan::kInvalidThreadID)180    Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);181  else182    Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);183  TraceLength = __gwp_asan_get_allocation_trace(184      AllocMeta, Trace, kMaximumStackFramesForCrashTrace);185  PrintBacktrace(Trace, TraceLength, Printf);186}187 188struct sigaction PreviousHandler;189bool SignalHandlerInstalled;190bool RecoverableSignal;191gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;192Printf_t PrintfForSignalHandler;193PrintBacktrace_t PrintBacktraceForSignalHandler;194SegvBacktrace_t BacktraceForSignalHandler;195 196static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {197  const gwp_asan::AllocatorState *State =198      GPAForSignalHandler->getAllocatorState();199  void *FaultAddr = info->si_addr;200  uintptr_t FaultAddrUPtr = reinterpret_cast<uintptr_t>(FaultAddr);201 202  if (__gwp_asan_error_is_mine(State, FaultAddrUPtr)) {203    GPAForSignalHandler->preCrashReport(FaultAddr);204 205    dumpReport(FaultAddrUPtr, State, GPAForSignalHandler->getMetadataRegion(),206               BacktraceForSignalHandler, PrintfForSignalHandler,207               PrintBacktraceForSignalHandler, ucontext);208 209    if (RecoverableSignal) {210      GPAForSignalHandler->postCrashReportRecoverableOnly(FaultAddr);211      return;212    }213  }214 215  // Process any previous handlers as long as the crash wasn't a GWP-ASan crash216  // in recoverable mode.217  if (PreviousHandler.sa_flags & SA_SIGINFO) {218    PreviousHandler.sa_sigaction(sig, info, ucontext);219  } else if (PreviousHandler.sa_handler == SIG_DFL) {220    // If the previous handler was the default handler, cause a core dump.221    signal(SIGSEGV, SIG_DFL);222    raise(SIGSEGV);223  } else if (PreviousHandler.sa_handler == SIG_IGN) {224    // If the previous segv handler was SIGIGN, crash iff we were responsible225    // for the crash.226    if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),227                                 reinterpret_cast<uintptr_t>(info->si_addr))) {228      signal(SIGSEGV, SIG_DFL);229      raise(SIGSEGV);230    }231  } else {232    PreviousHandler.sa_handler(sig);233  }234}235} // anonymous namespace236 237namespace gwp_asan {238namespace segv_handler {239 240void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,241                           PrintBacktrace_t PrintBacktrace,242                           SegvBacktrace_t SegvBacktrace, bool Recoverable) {243  assert(GPA && "GPA wasn't provided to installSignalHandlers.");244  assert(Printf && "Printf wasn't provided to installSignalHandlers.");245  assert(PrintBacktrace &&246         "PrintBacktrace wasn't provided to installSignalHandlers.");247  assert(SegvBacktrace &&248         "SegvBacktrace wasn't provided to installSignalHandlers.");249  GPAForSignalHandler = GPA;250  PrintfForSignalHandler = Printf;251  PrintBacktraceForSignalHandler = PrintBacktrace;252  BacktraceForSignalHandler = SegvBacktrace;253  RecoverableSignal = Recoverable;254 255  struct sigaction Action = {};256  Action.sa_sigaction = sigSegvHandler;257  Action.sa_flags = SA_SIGINFO;258  sigaction(SIGSEGV, &Action, &PreviousHandler);259  SignalHandlerInstalled = true;260  HasReportedBadPoolAccess = false;261}262 263void uninstallSignalHandlers() {264  if (SignalHandlerInstalled) {265    sigaction(SIGSEGV, &PreviousHandler, nullptr);266    SignalHandlerInstalled = false;267  }268}269} // namespace segv_handler270} // namespace gwp_asan271