123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- //===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "gwp_asan/common.h"
- #include "gwp_asan/crash_handler.h"
- #include "gwp_asan/guarded_pool_allocator.h"
- #include "gwp_asan/optional/segv_handler.h"
- #include "gwp_asan/options.h"
- // RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
- // macro is defined before including <inttypes.h>.
- #ifndef __STDC_FORMAT_MACROS
- #define __STDC_FORMAT_MACROS 1
- #endif
- #include <assert.h>
- #include <inttypes.h>
- #include <signal.h>
- #include <stdio.h>
- using gwp_asan::AllocationMetadata;
- using gwp_asan::Error;
- using gwp_asan::GuardedPoolAllocator;
- using gwp_asan::Printf_t;
- using gwp_asan::backtrace::PrintBacktrace_t;
- using gwp_asan::backtrace::SegvBacktrace_t;
- namespace {
- struct ScopedEndOfReportDecorator {
- ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
- ~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
- gwp_asan::Printf_t Printf;
- };
- // Prints the provided error and metadata information.
- void printHeader(Error E, uintptr_t AccessPtr,
- const gwp_asan::AllocationMetadata *Metadata,
- Printf_t Printf) {
- // Print using intermediate strings. Platforms like Android don't like when
- // you print multiple times to the same line, as there may be a newline
- // appended to a log file automatically per Printf() call.
- constexpr size_t kDescriptionBufferLen = 128;
- char DescriptionBuffer[kDescriptionBufferLen] = "";
- if (E != Error::UNKNOWN && Metadata != nullptr) {
- uintptr_t Address = __gwp_asan_get_allocation_address(Metadata);
- size_t Size = __gwp_asan_get_allocation_size(Metadata);
- if (E == Error::USE_AFTER_FREE) {
- snprintf(DescriptionBuffer, kDescriptionBufferLen,
- "(%zu byte%s into a %zu-byte allocation at 0x%zx) ",
- AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
- Address);
- } else if (AccessPtr < Address) {
- snprintf(DescriptionBuffer, kDescriptionBufferLen,
- "(%zu byte%s to the left of a %zu-byte allocation at 0x%zx) ",
- Address - AccessPtr, (Address - AccessPtr == 1) ? "" : "s", Size,
- Address);
- } else if (AccessPtr > Address) {
- snprintf(DescriptionBuffer, kDescriptionBufferLen,
- "(%zu byte%s to the right of a %zu-byte allocation at 0x%zx) ",
- AccessPtr - Address, (AccessPtr - Address == 1) ? "" : "s", Size,
- Address);
- } else {
- snprintf(DescriptionBuffer, kDescriptionBufferLen,
- "(a %zu-byte allocation) ", Size);
- }
- }
- // Possible number of digits of a 64-bit number: ceil(log10(2^64)) == 20. Add
- // a null terminator, and round to the nearest 8-byte boundary.
- uint64_t ThreadID = gwp_asan::getThreadID();
- constexpr size_t kThreadBufferLen = 24;
- char ThreadBuffer[kThreadBufferLen];
- if (ThreadID == gwp_asan::kInvalidThreadID)
- snprintf(ThreadBuffer, kThreadBufferLen, "<unknown>");
- else
- snprintf(ThreadBuffer, kThreadBufferLen, "%" PRIu64, ThreadID);
- Printf("%s at 0x%zx %sby thread %s here:\n", gwp_asan::ErrorToString(E),
- AccessPtr, DescriptionBuffer, ThreadBuffer);
- }
- void 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) {
- assert(State && "dumpReport missing Allocator State.");
- assert(Metadata && "dumpReport missing Metadata.");
- assert(Printf && "dumpReport missing Printf.");
- if (!__gwp_asan_error_is_mine(State, ErrorPtr))
- return;
- Printf("*** GWP-ASan detected a memory error ***\n");
- ScopedEndOfReportDecorator Decorator(Printf);
- uintptr_t InternalErrorPtr = __gwp_asan_get_internal_crash_address(State);
- if (InternalErrorPtr != 0u)
- ErrorPtr = InternalErrorPtr;
- Error E = __gwp_asan_diagnose_error(State, Metadata, ErrorPtr);
- if (E == Error::UNKNOWN) {
- Printf("GWP-ASan cannot provide any more information about this error. "
- "This may occur due to a wild memory access into the GWP-ASan pool, "
- "or an overflow/underflow that is > 512B in length.\n");
- return;
- }
- const gwp_asan::AllocationMetadata *AllocMeta =
- __gwp_asan_get_metadata(State, Metadata, ErrorPtr);
- // Print the error header.
- printHeader(E, ErrorPtr, AllocMeta, Printf);
- // Print the fault backtrace.
- static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
- uintptr_t Trace[kMaximumStackFramesForCrashTrace];
- size_t TraceLength =
- SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
- PrintBacktrace(Trace, TraceLength, Printf);
- if (AllocMeta == nullptr)
- return;
- // Maybe print the deallocation trace.
- if (__gwp_asan_is_deallocated(AllocMeta)) {
- uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
- if (ThreadID == gwp_asan::kInvalidThreadID)
- Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
- else
- Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
- TraceLength = __gwp_asan_get_deallocation_trace(
- AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
- PrintBacktrace(Trace, TraceLength, Printf);
- }
- // Print the allocation trace.
- uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
- if (ThreadID == gwp_asan::kInvalidThreadID)
- Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
- else
- Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
- TraceLength = __gwp_asan_get_allocation_trace(
- AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
- PrintBacktrace(Trace, TraceLength, Printf);
- }
- struct sigaction PreviousHandler;
- bool SignalHandlerInstalled;
- gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
- Printf_t PrintfForSignalHandler;
- PrintBacktrace_t PrintBacktraceForSignalHandler;
- SegvBacktrace_t BacktraceForSignalHandler;
- static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
- if (GPAForSignalHandler) {
- GPAForSignalHandler->stop();
- dumpReport(reinterpret_cast<uintptr_t>(info->si_addr),
- GPAForSignalHandler->getAllocatorState(),
- GPAForSignalHandler->getMetadataRegion(),
- BacktraceForSignalHandler, PrintfForSignalHandler,
- PrintBacktraceForSignalHandler, ucontext);
- }
- // Process any previous handlers.
- if (PreviousHandler.sa_flags & SA_SIGINFO) {
- PreviousHandler.sa_sigaction(sig, info, ucontext);
- } else if (PreviousHandler.sa_handler == SIG_DFL) {
- // If the previous handler was the default handler, cause a core dump.
- signal(SIGSEGV, SIG_DFL);
- raise(SIGSEGV);
- } else if (PreviousHandler.sa_handler == SIG_IGN) {
- // If the previous segv handler was SIGIGN, crash iff we were responsible
- // for the crash.
- if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
- reinterpret_cast<uintptr_t>(info->si_addr))) {
- signal(SIGSEGV, SIG_DFL);
- raise(SIGSEGV);
- }
- } else {
- PreviousHandler.sa_handler(sig);
- }
- }
- } // anonymous namespace
- namespace gwp_asan {
- namespace segv_handler {
- void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace,
- SegvBacktrace_t SegvBacktrace) {
- assert(GPA && "GPA wasn't provided to installSignalHandlers.");
- assert(Printf && "Printf wasn't provided to installSignalHandlers.");
- assert(PrintBacktrace &&
- "PrintBacktrace wasn't provided to installSignalHandlers.");
- assert(SegvBacktrace &&
- "SegvBacktrace wasn't provided to installSignalHandlers.");
- GPAForSignalHandler = GPA;
- PrintfForSignalHandler = Printf;
- PrintBacktraceForSignalHandler = PrintBacktrace;
- BacktraceForSignalHandler = SegvBacktrace;
- struct sigaction Action = {};
- Action.sa_sigaction = sigSegvHandler;
- Action.sa_flags = SA_SIGINFO;
- sigaction(SIGSEGV, &Action, &PreviousHandler);
- SignalHandlerInstalled = true;
- }
- void uninstallSignalHandlers() {
- if (SignalHandlerInstalled) {
- sigaction(SIGSEGV, &PreviousHandler, nullptr);
- SignalHandlerInstalled = false;
- }
- }
- } // namespace segv_handler
- } // namespace gwp_asan
|