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