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 return State->FailureAddress; 36 } 37 38 static const AllocationMetadata * 39 addrToMetadata(const gwp_asan::AllocatorState *State, 40 const AllocationMetadata *Metadata, uintptr_t Ptr) { 41 // Note - Similar implementation in guarded_pool_allocator.cpp. 42 return &Metadata[State->getNearestSlot(Ptr)]; 43 } 44 45 gwp_asan::Error 46 __gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State, 47 const gwp_asan::AllocationMetadata *Metadata, 48 uintptr_t ErrorPtr) { 49 if (!__gwp_asan_error_is_mine(State, ErrorPtr)) 50 return Error::UNKNOWN; 51 52 if (State->FailureType != Error::UNKNOWN) 53 return State->FailureType; 54 55 // Let's try and figure out what the source of this error is. 56 if (State->isGuardPage(ErrorPtr)) { 57 size_t Slot = State->getNearestSlot(ErrorPtr); 58 const AllocationMetadata *SlotMeta = 59 addrToMetadata(State, Metadata, State->slotToAddr(Slot)); 60 61 // Ensure that this slot was allocated once upon a time. 62 if (!SlotMeta->Addr) 63 return Error::UNKNOWN; 64 65 if (SlotMeta->Addr < ErrorPtr) 66 return Error::BUFFER_OVERFLOW; 67 return Error::BUFFER_UNDERFLOW; 68 } 69 70 // Access wasn't a guard page, check for use-after-free. 71 const AllocationMetadata *SlotMeta = 72 addrToMetadata(State, Metadata, ErrorPtr); 73 if (SlotMeta->IsDeallocated) { 74 return Error::USE_AFTER_FREE; 75 } 76 77 // If we have reached here, the error is still unknown. 78 return Error::UNKNOWN; 79 } 80 81 const gwp_asan::AllocationMetadata * 82 __gwp_asan_get_metadata(const gwp_asan::AllocatorState *State, 83 const gwp_asan::AllocationMetadata *Metadata, 84 uintptr_t ErrorPtr) { 85 if (!__gwp_asan_error_is_mine(State, ErrorPtr)) 86 return nullptr; 87 88 if (ErrorPtr >= State->GuardedPagePoolEnd || 89 State->GuardedPagePool > ErrorPtr) 90 return nullptr; 91 92 const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr); 93 if (Meta->Addr == 0) 94 return nullptr; 95 96 return Meta; 97 } 98 99 uintptr_t __gwp_asan_get_allocation_address( 100 const gwp_asan::AllocationMetadata *AllocationMeta) { 101 return AllocationMeta->Addr; 102 } 103 104 size_t __gwp_asan_get_allocation_size( 105 const gwp_asan::AllocationMetadata *AllocationMeta) { 106 return AllocationMeta->RequestedSize; 107 } 108 109 uint64_t __gwp_asan_get_allocation_thread_id( 110 const gwp_asan::AllocationMetadata *AllocationMeta) { 111 return AllocationMeta->AllocationTrace.ThreadID; 112 } 113 114 size_t __gwp_asan_get_allocation_trace( 115 const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer, 116 size_t BufferLen) { 117 uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect]; 118 size_t UnpackedLength = gwp_asan::compression::unpack( 119 AllocationMeta->AllocationTrace.CompressedTrace, 120 AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer, 121 AllocationMetadata::kMaxTraceLengthToCollect); 122 if (UnpackedLength < BufferLen) 123 BufferLen = UnpackedLength; 124 memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer)); 125 return UnpackedLength; 126 } 127 128 bool __gwp_asan_is_deallocated( 129 const gwp_asan::AllocationMetadata *AllocationMeta) { 130 return AllocationMeta->IsDeallocated; 131 } 132 133 uint64_t __gwp_asan_get_deallocation_thread_id( 134 const gwp_asan::AllocationMetadata *AllocationMeta) { 135 return AllocationMeta->DeallocationTrace.ThreadID; 136 } 137 138 size_t __gwp_asan_get_deallocation_trace( 139 const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer, 140 size_t BufferLen) { 141 uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect]; 142 size_t UnpackedLength = gwp_asan::compression::unpack( 143 AllocationMeta->DeallocationTrace.CompressedTrace, 144 AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer, 145 AllocationMetadata::kMaxTraceLengthToCollect); 146 if (UnpackedLength < BufferLen) 147 BufferLen = UnpackedLength; 148 memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer)); 149 return UnpackedLength; 150 } 151 152 #ifdef __cplusplus 153 } // extern "C" 154 #endif 155