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