1 //===-- crash_handler.cpp ---------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "gwp_asan/common.h"
10 #include "gwp_asan/stack_trace_compressor.h"
16 using AllocationMetadata
= gwp_asan::AllocationMetadata
;
17 using Error
= gwp_asan::Error
;
23 bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState
*State
,
25 assert(State
&& "State should not be nullptr.");
26 if (State
->FailureType
!= Error::UNKNOWN
&& State
->FailureAddress
!= 0)
29 return ErrorPtr
< State
->GuardedPagePoolEnd
&&
30 State
->GuardedPagePool
<= ErrorPtr
;
34 __gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState
*State
,
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
41 if (ErrorPtr
!= State
->internallyDetectedErrorFaultAddress())
43 return State
->FailureAddress
;
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
)];
54 __gwp_asan_diagnose_error(const gwp_asan::AllocatorState
*State
,
55 const gwp_asan::AllocationMetadata
*Metadata
,
57 if (!__gwp_asan_error_is_mine(State
, ErrorPtr
))
58 return Error::UNKNOWN
;
60 if (State
->FailureType
!= Error::UNKNOWN
)
61 return State
->FailureType
;
63 // Check for use-after-free.
64 if (addrToMetadata(State
, Metadata
, ErrorPtr
)->IsDeallocated
)
65 return Error::USE_AFTER_FREE
;
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
));
76 // Ensure that this slot was allocated once upon a time.
78 return Error::UNKNOWN
;
80 if (SlotMeta
->Addr
< ErrorPtr
)
81 return Error::BUFFER_OVERFLOW
;
82 return Error::BUFFER_UNDERFLOW
;
85 // If we have reached here, the error is still unknown.
86 return Error::UNKNOWN
;
89 const gwp_asan::AllocationMetadata
*
90 __gwp_asan_get_metadata(const gwp_asan::AllocatorState
*State
,
91 const gwp_asan::AllocationMetadata
*Metadata
,
93 if (!__gwp_asan_error_is_mine(State
, ErrorPtr
))
96 if (ErrorPtr
>= State
->GuardedPagePoolEnd
||
97 State
->GuardedPagePool
> ErrorPtr
)
100 const AllocationMetadata
*Meta
= addrToMetadata(State
, Metadata
, ErrorPtr
);
107 uintptr_t __gwp_asan_get_allocation_address(
108 const gwp_asan::AllocationMetadata
*AllocationMeta
) {
109 return AllocationMeta
->Addr
;
112 size_t __gwp_asan_get_allocation_size(
113 const gwp_asan::AllocationMetadata
*AllocationMeta
) {
114 return AllocationMeta
->RequestedSize
;
117 uint64_t __gwp_asan_get_allocation_thread_id(
118 const gwp_asan::AllocationMetadata
*AllocationMeta
) {
119 return AllocationMeta
->AllocationTrace
.ThreadID
;
122 size_t __gwp_asan_get_allocation_trace(
123 const gwp_asan::AllocationMetadata
*AllocationMeta
, uintptr_t *Buffer
,
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
;
136 bool __gwp_asan_is_deallocated(
137 const gwp_asan::AllocationMetadata
*AllocationMeta
) {
138 return AllocationMeta
->IsDeallocated
;
141 uint64_t __gwp_asan_get_deallocation_thread_id(
142 const gwp_asan::AllocationMetadata
*AllocationMeta
) {
143 return AllocationMeta
->DeallocationTrace
.ThreadID
;
146 size_t __gwp_asan_get_deallocation_trace(
147 const gwp_asan::AllocationMetadata
*AllocationMeta
, uintptr_t *Buffer
,
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
;