xref: /freebsd/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
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 
__gwp_asan_error_is_mine(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)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
__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)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 *
addrToMetadata(const gwp_asan::AllocatorState * State,const AllocationMetadata * Metadata,uintptr_t Ptr)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
__gwp_asan_diagnose_error(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)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 *
__gwp_asan_get_metadata(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)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 
__gwp_asan_get_allocation_address(const gwp_asan::AllocationMetadata * AllocationMeta)107 uintptr_t __gwp_asan_get_allocation_address(
108     const gwp_asan::AllocationMetadata *AllocationMeta) {
109   return AllocationMeta->Addr;
110 }
111 
__gwp_asan_get_allocation_size(const gwp_asan::AllocationMetadata * AllocationMeta)112 size_t __gwp_asan_get_allocation_size(
113     const gwp_asan::AllocationMetadata *AllocationMeta) {
114   return AllocationMeta->RequestedSize;
115 }
116 
__gwp_asan_get_allocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)117 uint64_t __gwp_asan_get_allocation_thread_id(
118     const gwp_asan::AllocationMetadata *AllocationMeta) {
119   return AllocationMeta->AllocationTrace.ThreadID;
120 }
121 
__gwp_asan_get_allocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)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 
__gwp_asan_is_deallocated(const gwp_asan::AllocationMetadata * AllocationMeta)136 bool __gwp_asan_is_deallocated(
137     const gwp_asan::AllocationMetadata *AllocationMeta) {
138   return AllocationMeta->IsDeallocated;
139 }
140 
__gwp_asan_get_deallocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)141 uint64_t __gwp_asan_get_deallocation_thread_id(
142     const gwp_asan::AllocationMetadata *AllocationMeta) {
143   return AllocationMeta->DeallocationTrace.ThreadID;
144 }
145 
__gwp_asan_get_deallocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)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