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-exception 6 // 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 16 using AllocationMetadata = gwp_asan::AllocationMetadata; 17 using Error = gwp_asan::Error; 18 19 #ifdef __cplusplus 20 extern "C" { 21 #endif 22 23 bool __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 33 uintptr_t 34 __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. The 37 // fault address from the signal handler is used to discriminate whether it's 38 // internally- or externally-raised, and the pool maintains a special page at 39 // the end of the GuardedPagePool specifically for the internally-raised 40 // faults. 41 if (ErrorPtr != State->internallyDetectedErrorFaultAddress()) 42 return 0u; 43 return State->FailureAddress; 44 } 45 46 static const AllocationMetadata * 47 addrToMetadata(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 53 gwp_asan::Error 54 __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/right 68 // page placement, we can have buffer-overflows that don't touch a guarded 69 // page, but these are not possible to detect unless it's also a 70 // 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 89 const 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 107 uintptr_t __gwp_asan_get_allocation_address( 108 const gwp_asan::AllocationMetadata *AllocationMeta) { 109 return AllocationMeta->Addr; 110 } 111 112 size_t __gwp_asan_get_allocation_size( 113 const gwp_asan::AllocationMetadata *AllocationMeta) { 114 return AllocationMeta->RequestedSize; 115 } 116 117 uint64_t __gwp_asan_get_allocation_thread_id( 118 const gwp_asan::AllocationMetadata *AllocationMeta) { 119 return AllocationMeta->AllocationTrace.ThreadID; 120 } 121 122 size_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 136 bool __gwp_asan_is_deallocated( 137 const gwp_asan::AllocationMetadata *AllocationMeta) { 138 return AllocationMeta->IsDeallocated; 139 } 140 141 uint64_t __gwp_asan_get_deallocation_thread_id( 142 const gwp_asan::AllocationMetadata *AllocationMeta) { 143 return AllocationMeta->DeallocationTrace.ThreadID; 144 } 145 146 size_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 __cplusplus 161 } // extern "C" 162 #endif 163