1 | //===-- common.h ------------------------------------------------*- 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 | // This file contains code that is common between the crash handler and the |
10 | // GuardedPoolAllocator. |
11 | |
12 | #ifndef GWP_ASAN_COMMON_H_ |
13 | #define GWP_ASAN_COMMON_H_ |
14 | |
15 | #include "gwp_asan/definitions.h" |
16 | #include "gwp_asan/options.h" |
17 | |
18 | #include <stddef.h> |
19 | #include <stdint.h> |
20 | |
21 | namespace gwp_asan { |
22 | |
23 | // Magic header that resides in the AllocatorState so that GWP-ASan bugreports |
24 | // can be understood by tools at different versions. Out-of-process crash |
25 | // handlers, like crashpad on Fuchsia, take the raw contents of the |
26 | // AllocationMetatada array and the AllocatorState, and shove them into the |
27 | // minidump. Online unpacking of these structs needs to know from which version |
28 | // of GWP-ASan it's extracting the information, as the structures are not |
29 | // stable. |
30 | struct AllocatorVersionMagic { |
31 | // The values are copied into the structure at runtime, during |
32 | // `GuardedPoolAllocator::init()` so that GWP-ASan remains completely in the |
33 | // `.bss` segment. |
34 | static constexpr uint8_t kAllocatorVersionMagic[4] = {'A', 'S', 'A', 'N'}; |
35 | uint8_t Magic[4] = {}; |
36 | // Update the version number when the AllocatorState or AllocationMetadata |
37 | // change. |
38 | static constexpr uint16_t kAllocatorVersion = 2; |
39 | uint16_t Version = 0; |
40 | uint16_t Reserved = 0; |
41 | }; |
42 | |
43 | enum class Error : uint8_t { |
44 | UNKNOWN, |
45 | USE_AFTER_FREE, |
46 | DOUBLE_FREE, |
47 | INVALID_FREE, |
48 | BUFFER_OVERFLOW, |
49 | BUFFER_UNDERFLOW |
50 | }; |
51 | |
52 | const char *ErrorToString(const Error &E); |
53 | |
54 | static constexpr uint64_t kInvalidThreadID = UINT64_MAX; |
55 | // Get the current thread ID, or kInvalidThreadID if failure. Note: This |
56 | // implementation is platform-specific. |
57 | uint64_t getThreadID(); |
58 | |
59 | // This struct contains all the metadata recorded about a single allocation made |
60 | // by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid. |
61 | struct AllocationMetadata { |
62 | // The number of bytes used to store a compressed stack frame. On 64-bit |
63 | // platforms, assuming a compression ratio of 50%, this should allow us to |
64 | // store ~64 frames per trace. |
65 | static constexpr size_t kStackFrameStorageBytes = 256; |
66 | |
67 | // Maximum number of stack frames to collect on allocation/deallocation. The |
68 | // actual number of collected frames may be less than this as the stack |
69 | // frames are compressed into a fixed memory range. |
70 | static constexpr size_t kMaxTraceLengthToCollect = 128; |
71 | |
72 | // Records the given allocation metadata into this struct. |
73 | void RecordAllocation(uintptr_t Addr, size_t RequestedSize); |
74 | // Record that this allocation is now deallocated. |
75 | void RecordDeallocation(); |
76 | |
77 | struct CallSiteInfo { |
78 | // Record the current backtrace to this callsite. |
79 | void RecordBacktrace(options::Backtrace_t Backtrace); |
80 | |
81 | // The compressed backtrace to the allocation/deallocation. |
82 | uint8_t CompressedTrace[kStackFrameStorageBytes]; |
83 | // The thread ID for this trace, or kInvalidThreadID if not available. |
84 | uint64_t ThreadID = kInvalidThreadID; |
85 | // The size of the compressed trace (in bytes). Zero indicates that no |
86 | // trace was collected. |
87 | size_t TraceSize = 0; |
88 | }; |
89 | |
90 | // The address of this allocation. If zero, the rest of this struct isn't |
91 | // valid, as the allocation has never occurred. |
92 | uintptr_t Addr = 0; |
93 | // Represents the actual size of the allocation. |
94 | size_t RequestedSize = 0; |
95 | |
96 | CallSiteInfo AllocationTrace; |
97 | CallSiteInfo DeallocationTrace; |
98 | |
99 | // Whether this allocation has been deallocated yet. |
100 | bool IsDeallocated = false; |
101 | |
102 | // In recoverable mode, whether this allocation has had a crash associated |
103 | // with it. This has certain side effects, like meaning this allocation will |
104 | // permanently occupy a slot, and won't ever have another crash reported from |
105 | // it. |
106 | bool HasCrashed = false; |
107 | }; |
108 | |
109 | // This holds the state that's shared between the GWP-ASan allocator and the |
110 | // crash handler. This, in conjunction with the Metadata array, forms the entire |
111 | // set of information required for understanding a GWP-ASan crash. |
112 | struct AllocatorState { |
113 | constexpr AllocatorState() {} |
114 | AllocatorVersionMagic VersionMagic{}; |
115 | |
116 | // Returns whether the provided pointer is a current sampled allocation that |
117 | // is owned by this pool. |
118 | GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const { |
119 | uintptr_t P = reinterpret_cast<uintptr_t>(Ptr); |
120 | return P < GuardedPagePoolEnd && GuardedPagePool <= P; |
121 | } |
122 | |
123 | // Returns the address of the N-th guarded slot. |
124 | uintptr_t slotToAddr(size_t N) const; |
125 | |
126 | // Returns the largest allocation that is supported by this pool. |
127 | size_t maximumAllocationSize() const; |
128 | |
129 | // Gets the nearest slot to the provided address. |
130 | size_t getNearestSlot(uintptr_t Ptr) const; |
131 | |
132 | // Returns whether the provided pointer is a guard page or not. The pointer |
133 | // must be within memory owned by this pool, else the result is undefined. |
134 | bool isGuardPage(uintptr_t Ptr) const; |
135 | |
136 | // Returns the address that's used by __gwp_asan_get_internal_crash_address() |
137 | // and GPA::raiseInternallyDetectedError() to communicate that the SEGV in |
138 | // question comes from an internally-detected error. |
139 | uintptr_t internallyDetectedErrorFaultAddress() const; |
140 | |
141 | // The number of guarded slots that this pool holds. |
142 | size_t MaxSimultaneousAllocations = 0; |
143 | |
144 | // Pointer to the pool of guarded slots. Note that this points to the start of |
145 | // the pool (which is a guard page), not a pointer to the first guarded page. |
146 | uintptr_t GuardedPagePool = 0; |
147 | uintptr_t GuardedPagePoolEnd = 0; |
148 | |
149 | // Cached page size for this system in bytes. |
150 | size_t PageSize = 0; |
151 | |
152 | // The type and address of an internally-detected failure. For INVALID_FREE |
153 | // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set |
154 | // these values and terminate the process. |
155 | Error FailureType = Error::UNKNOWN; |
156 | uintptr_t FailureAddress = 0; |
157 | }; |
158 | |
159 | // Below are various compile-time checks that the layout of the internal |
160 | // GWP-ASan structures are undisturbed. If they are disturbed, the version magic |
161 | // number needs to be increased by one, and the asserts need to be updated. |
162 | // Out-of-process crash handlers, like breakpad/crashpad, may copy the internal |
163 | // GWP-ASan structures into a minidump for offline reconstruction of the crash. |
164 | // In order to accomplish this, the offline reconstructor needs to know the |
165 | // version of GWP-ASan internal structures that it's unpacking (along with the |
166 | // architecture-specific layout info, which is left as an exercise to the crash |
167 | // handler). |
168 | static_assert(offsetof(AllocatorState, VersionMagic) == 0, "" ); |
169 | static_assert(sizeof(AllocatorVersionMagic) == 8, "" ); |
170 | #if defined(__x86_64__) |
171 | static_assert(sizeof(AllocatorState) == 56, "" ); |
172 | static_assert(offsetof(AllocatorState, FailureAddress) == 48, "" ); |
173 | static_assert(sizeof(AllocationMetadata) == 568, "" ); |
174 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "" ); |
175 | #elif defined(__aarch64__) |
176 | static_assert(sizeof(AllocatorState) == 56, "" ); |
177 | static_assert(offsetof(AllocatorState, FailureAddress) == 48, "" ); |
178 | static_assert(sizeof(AllocationMetadata) == 568, "" ); |
179 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "" ); |
180 | #elif defined(__i386__) |
181 | static_assert(sizeof(AllocatorState) == 32, "" ); |
182 | static_assert(offsetof(AllocatorState, FailureAddress) == 28, "" ); |
183 | static_assert(sizeof(AllocationMetadata) == 548, "" ); |
184 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 544, "" ); |
185 | #elif defined(__arm__) |
186 | static_assert(sizeof(AllocatorState) == 32, "" ); |
187 | static_assert(offsetof(AllocatorState, FailureAddress) == 28, "" ); |
188 | static_assert(sizeof(AllocationMetadata) == 560, "" ); |
189 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 552, "" ); |
190 | #endif // defined($ARCHITECTURE) |
191 | |
192 | } // namespace gwp_asan |
193 | #endif // GWP_ASAN_COMMON_H_ |
194 | |