163 lines · cpp
1//===-- crash_handler.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/stack_trace_compressor.h"11 12#include <assert.h>13#include <stdint.h>14#include <string.h>15 16using AllocationMetadata = gwp_asan::AllocationMetadata;17using Error = gwp_asan::Error;18 19#ifdef __cplusplus20extern "C" {21#endif22 23bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState *State,24 uintptr_t ErrorPtr) {25 assert(State && "State should not be nullptr.");26 if (State->FailureType != Error::UNKNOWN && State->FailureAddress != 0)27 return true;28 29 return ErrorPtr < State->GuardedPagePoolEnd &&30 State->GuardedPagePool <= ErrorPtr;31}32 33uintptr_t34__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State,35 uintptr_t ErrorPtr) {36 // There can be a race between internally- and externally-raised faults. The37 // fault address from the signal handler is used to discriminate whether it's38 // internally- or externally-raised, and the pool maintains a special page at39 // the end of the GuardedPagePool specifically for the internally-raised40 // faults.41 if (ErrorPtr != State->internallyDetectedErrorFaultAddress())42 return 0u;43 return State->FailureAddress;44}45 46static const AllocationMetadata *47addrToMetadata(const gwp_asan::AllocatorState *State,48 const AllocationMetadata *Metadata, uintptr_t Ptr) {49 // Note - Similar implementation in guarded_pool_allocator.cpp.50 return &Metadata[State->getNearestSlot(Ptr)];51}52 53gwp_asan::Error54__gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State,55 const gwp_asan::AllocationMetadata *Metadata,56 uintptr_t ErrorPtr) {57 if (!__gwp_asan_error_is_mine(State, ErrorPtr))58 return Error::UNKNOWN;59 60 if (State->FailureType != Error::UNKNOWN)61 return State->FailureType;62 63 // Check for use-after-free.64 if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated)65 return Error::USE_AFTER_FREE;66 67 // Check for buffer-overflow. Because of allocation alignment or left/right68 // page placement, we can have buffer-overflows that don't touch a guarded69 // page, but these are not possible to detect unless it's also a70 // use-after-free, which is handled above.71 if (State->isGuardPage(ErrorPtr)) {72 size_t Slot = State->getNearestSlot(ErrorPtr);73 const AllocationMetadata *SlotMeta =74 addrToMetadata(State, Metadata, State->slotToAddr(Slot));75 76 // Ensure that this slot was allocated once upon a time.77 if (!SlotMeta->Addr)78 return Error::UNKNOWN;79 80 if (SlotMeta->Addr < ErrorPtr)81 return Error::BUFFER_OVERFLOW;82 return Error::BUFFER_UNDERFLOW;83 }84 85 // If we have reached here, the error is still unknown.86 return Error::UNKNOWN;87}88 89const gwp_asan::AllocationMetadata *90__gwp_asan_get_metadata(const gwp_asan::AllocatorState *State,91 const gwp_asan::AllocationMetadata *Metadata,92 uintptr_t ErrorPtr) {93 if (!__gwp_asan_error_is_mine(State, ErrorPtr))94 return nullptr;95 96 if (ErrorPtr >= State->GuardedPagePoolEnd ||97 State->GuardedPagePool > ErrorPtr)98 return nullptr;99 100 const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr);101 if (Meta->Addr == 0)102 return nullptr;103 104 return Meta;105}106 107uintptr_t __gwp_asan_get_allocation_address(108 const gwp_asan::AllocationMetadata *AllocationMeta) {109 return AllocationMeta->Addr;110}111 112size_t __gwp_asan_get_allocation_size(113 const gwp_asan::AllocationMetadata *AllocationMeta) {114 return AllocationMeta->RequestedSize;115}116 117uint64_t __gwp_asan_get_allocation_thread_id(118 const gwp_asan::AllocationMetadata *AllocationMeta) {119 return AllocationMeta->AllocationTrace.ThreadID;120}121 122size_t __gwp_asan_get_allocation_trace(123 const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,124 size_t BufferLen) {125 uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];126 size_t UnpackedLength = gwp_asan::compression::unpack(127 AllocationMeta->AllocationTrace.CompressedTrace,128 AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,129 AllocationMetadata::kMaxTraceLengthToCollect);130 if (UnpackedLength < BufferLen)131 BufferLen = UnpackedLength;132 memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));133 return UnpackedLength;134}135 136bool __gwp_asan_is_deallocated(137 const gwp_asan::AllocationMetadata *AllocationMeta) {138 return AllocationMeta->IsDeallocated;139}140 141uint64_t __gwp_asan_get_deallocation_thread_id(142 const gwp_asan::AllocationMetadata *AllocationMeta) {143 return AllocationMeta->DeallocationTrace.ThreadID;144}145 146size_t __gwp_asan_get_deallocation_trace(147 const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,148 size_t BufferLen) {149 uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];150 size_t UnpackedLength = gwp_asan::compression::unpack(151 AllocationMeta->DeallocationTrace.CompressedTrace,152 AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,153 AllocationMetadata::kMaxTraceLengthToCollect);154 if (UnpackedLength < BufferLen)155 BufferLen = UnpackedLength;156 memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));157 return UnpackedLength;158}159 160#ifdef __cplusplus161} // extern "C"162#endif163