1 | //===-- guarded_pool_allocator.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 | #ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ |
10 | #define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ |
11 | |
12 | #include "gwp_asan/common.h" |
13 | #include "gwp_asan/definitions.h" |
14 | #include "gwp_asan/mutex.h" |
15 | #include "gwp_asan/options.h" |
16 | #include "gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h" // IWYU pragma: keep |
17 | #include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h" // IWYU pragma: keep |
18 | #include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h" |
19 | |
20 | #include <stddef.h> |
21 | #include <stdint.h> |
22 | // IWYU pragma: no_include <__stddef_max_align_t.h> |
23 | // IWYU pragma: no_include <__stddef_null.h> |
24 | // IWYU pragma: no_include <__stddef_nullptr_t.h> |
25 | // IWYU pragma: no_include <__stddef_offsetof.h> |
26 | // IWYU pragma: no_include <__stddef_ptrdiff_t.h> |
27 | // IWYU pragma: no_include <__stddef_rsize_t.h> |
28 | // IWYU pragma: no_include <__stddef_size_t.h> |
29 | // IWYU pragma: no_include <__stddef_unreachable.h> |
30 | // IWYU pragma: no_include <__stddef_wchar_t.h> |
31 | // IWYU pragma: no_include <__stddef_wint_t.h> |
32 | |
33 | namespace gwp_asan { |
34 | // This class is the primary implementation of the allocator portion of GWP- |
35 | // ASan. It is the sole owner of the pool of sequentially allocated guarded |
36 | // slots. It should always be treated as a singleton. |
37 | |
38 | // Functions in the public interface of this class are thread-compatible until |
39 | // init() is called, at which point they become thread-safe (unless specified |
40 | // otherwise). |
41 | class GuardedPoolAllocator { |
42 | public: |
43 | // Name of the GWP-ASan mapping that for `Metadata`. |
44 | static constexpr const char *kGwpAsanMetadataName = "GWP-ASan Metadata" ; |
45 | |
46 | // During program startup, we must ensure that memory allocations do not land |
47 | // in this allocation pool if the allocator decides to runtime-disable |
48 | // GWP-ASan. The constructor value-initialises the class such that if no |
49 | // further initialisation takes place, calls to shouldSample() and |
50 | // pointerIsMine() will return false. |
51 | constexpr GuardedPoolAllocator() {} |
52 | GuardedPoolAllocator(const GuardedPoolAllocator &) = delete; |
53 | GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete; |
54 | |
55 | // Note: This class is expected to be a singleton for the lifetime of the |
56 | // program. If this object is initialised, it will leak the guarded page pool |
57 | // and metadata allocations during destruction. We can't clean up these areas |
58 | // as this may cause a use-after-free on shutdown. |
59 | ~GuardedPoolAllocator() = default; |
60 | |
61 | // Initialise the rest of the members of this class. Create the allocation |
62 | // pool using the provided options. See options.inc for runtime configuration |
63 | // options. |
64 | void init(const options::Options &Opts); |
65 | void uninitTestOnly(); |
66 | |
67 | // Functions exported for libmemunreachable's use on Android. disable() |
68 | // installs a lock in the allocator that prevents any thread from being able |
69 | // to allocate memory, until enable() is called. |
70 | void disable(); |
71 | void enable(); |
72 | |
73 | typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg); |
74 | // Execute the callback Cb for every allocation the lies in [Base, Base + |
75 | // Size). Must be called while the allocator is disabled. The callback can not |
76 | // allocate. |
77 | void iterate(void *Base, size_t Size, iterate_callback Cb, void *Arg); |
78 | |
79 | // Return whether the allocation should be randomly chosen for sampling. |
80 | GWP_ASAN_ALWAYS_INLINE bool shouldSample() { |
81 | // NextSampleCounter == 0 means we "should regenerate the counter". |
82 | // == 1 means we "should sample this allocation". |
83 | // AdjustedSampleRatePlusOne is designed to intentionally underflow. This |
84 | // class must be valid when zero-initialised, and we wish to sample as |
85 | // infrequently as possible when this is the case, hence we underflow to |
86 | // UINT32_MAX. |
87 | if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0)) |
88 | getThreadLocals()->NextSampleCounter = |
89 | ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) & |
90 | ThreadLocalPackedVariables::NextSampleCounterMask; |
91 | |
92 | return GWP_ASAN_UNLIKELY(--getThreadLocals()->NextSampleCounter == 0); |
93 | } |
94 | |
95 | // Returns whether the provided pointer is a current sampled allocation that |
96 | // is owned by this pool. |
97 | GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const { |
98 | return State.pointerIsMine(Ptr); |
99 | } |
100 | |
101 | // Allocate memory in a guarded slot, with the specified `Alignment`. Returns |
102 | // nullptr if the pool is empty, if the alignnment is not a power of two, or |
103 | // if the size/alignment makes the allocation too large for this pool to |
104 | // handle. By default, uses strong alignment (i.e. `max_align_t`), see |
105 | // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for discussion of |
106 | // alignment issues in the standard. |
107 | void *allocate(size_t Size, size_t Alignment = alignof(max_align_t)); |
108 | |
109 | // Deallocate memory in a guarded slot. The provided pointer must have been |
110 | // allocated using this pool. This will set the guarded slot as inaccessible. |
111 | void deallocate(void *Ptr); |
112 | |
113 | // Returns the size of the allocation at Ptr. |
114 | size_t getSize(const void *Ptr); |
115 | |
116 | // Returns a pointer to the Metadata region, or nullptr if it doesn't exist. |
117 | const AllocationMetadata *getMetadataRegion() const { return Metadata; } |
118 | |
119 | // Returns a pointer to the AllocatorState region. |
120 | const AllocatorState *getAllocatorState() const { return &State; } |
121 | |
122 | // Functions that the signal handler is responsible for calling, while |
123 | // providing the SEGV pointer, prior to dumping the crash, and after dumping |
124 | // the crash (in recoverable mode only). |
125 | void preCrashReport(void *Ptr); |
126 | void postCrashReportRecoverableOnly(void *Ptr); |
127 | |
128 | // Exposed as protected for testing. |
129 | protected: |
130 | // Returns the actual allocation size required to service an allocation with |
131 | // the provided Size and Alignment. |
132 | static size_t getRequiredBackingSize(size_t Size, size_t Alignment, |
133 | size_t PageSize); |
134 | |
135 | // Returns the provided pointer that meets the specified alignment, depending |
136 | // on whether it's left or right aligned. |
137 | static uintptr_t alignUp(uintptr_t Ptr, size_t Alignment); |
138 | static uintptr_t alignDown(uintptr_t Ptr, size_t Alignment); |
139 | |
140 | private: |
141 | // Name of actively-occupied slot mappings. |
142 | static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot" ; |
143 | // Name of the guard pages. This includes all slots that are not actively in |
144 | // use (i.e. were never used, or have been free()'d).) |
145 | static constexpr const char *kGwpAsanGuardPageName = "GWP-ASan Guard Page" ; |
146 | // Name of the mapping for `FreeSlots`. |
147 | static constexpr const char *kGwpAsanFreeSlotsName = "GWP-ASan Metadata" ; |
148 | |
149 | static constexpr size_t kInvalidSlotID = SIZE_MAX; |
150 | |
151 | // These functions anonymously map memory or change the permissions of mapped |
152 | // memory into this process in a platform-specific way. Pointer and size |
153 | // arguments are expected to be page-aligned. These functions will never |
154 | // return on error, instead electing to kill the calling process on failure. |
155 | // The pool memory is initially reserved and inaccessible, and RW mappings are |
156 | // subsequently created and destroyed via allocateInGuardedPool() and |
157 | // deallocateInGuardedPool(). Each mapping is named on platforms that support |
158 | // it, primarily Android. This name must be a statically allocated string, as |
159 | // the Android kernel uses the string pointer directly. |
160 | void *map(size_t Size, const char *Name) const; |
161 | void unmap(void *Ptr, size_t Size) const; |
162 | |
163 | // The pool is managed separately, as some platforms (particularly Fuchsia) |
164 | // manage virtual memory regions as a chunk where individual pages can still |
165 | // have separate permissions. These platforms maintain metadata about the |
166 | // region in order to perform operations. The pool is unique as it's the only |
167 | // thing in GWP-ASan that treats pages in a single VM region on an individual |
168 | // basis for page protection. |
169 | // The pointer returned by reserveGuardedPool() is the reserved address range |
170 | // of (at least) Size bytes. |
171 | void *reserveGuardedPool(size_t Size); |
172 | // allocateInGuardedPool() Ptr and Size must be a subrange of the previously |
173 | // reserved pool range. |
174 | void allocateInGuardedPool(void *Ptr, size_t Size) const; |
175 | // deallocateInGuardedPool() Ptr and Size must be an exact pair previously |
176 | // passed to allocateInGuardedPool(). |
177 | void deallocateInGuardedPool(void *Ptr, size_t Size) const; |
178 | void unreserveGuardedPool(); |
179 | |
180 | // Get the page size from the platform-specific implementation. Only needs to |
181 | // be called once, and the result should be cached in PageSize in this class. |
182 | static size_t getPlatformPageSize(); |
183 | |
184 | // Returns a pointer to the metadata for the owned pointer. If the pointer is |
185 | // not owned by this pool, the result is undefined. |
186 | AllocationMetadata *addrToMetadata(uintptr_t Ptr) const; |
187 | |
188 | // Reserve a slot for a new guarded allocation. Returns kInvalidSlotID if no |
189 | // slot is available to be reserved. |
190 | size_t reserveSlot(); |
191 | |
192 | // Unreserve the guarded slot. |
193 | void freeSlot(size_t SlotIndex); |
194 | |
195 | // Raise a SEGV and set the corresponding fields in the Allocator's State in |
196 | // order to tell the crash handler what happened. Used when errors are |
197 | // detected internally (Double Free, Invalid Free). |
198 | void raiseInternallyDetectedError(uintptr_t Address, Error E); |
199 | |
200 | static GuardedPoolAllocator *getSingleton(); |
201 | |
202 | // Install a pthread_atfork handler. |
203 | void installAtFork(); |
204 | |
205 | gwp_asan::AllocatorState State; |
206 | |
207 | // A mutex to protect the guarded slot and metadata pool for this class. |
208 | Mutex PoolMutex; |
209 | // Some unwinders can grab the libdl lock. In order to provide atfork |
210 | // protection, we need to ensure that we allow an unwinding thread to release |
211 | // the libdl lock before forking. |
212 | Mutex BacktraceMutex; |
213 | // Record the number allocations that we've sampled. We store this amount so |
214 | // that we don't randomly choose to recycle a slot that previously had an |
215 | // allocation before all the slots have been utilised. |
216 | size_t NumSampledAllocations = 0; |
217 | // Pointer to the allocation metadata (allocation/deallocation stack traces), |
218 | // if any. |
219 | AllocationMetadata *Metadata = nullptr; |
220 | |
221 | // Pointer to an array of free slot indexes. |
222 | size_t *FreeSlots = nullptr; |
223 | // The current length of the list of free slots. |
224 | size_t FreeSlotsLength = 0; |
225 | |
226 | // See options.{h, inc} for more information. |
227 | bool PerfectlyRightAlign = false; |
228 | |
229 | // Backtrace function provided by the supporting allocator. See `options.h` |
230 | // for more information. |
231 | options::Backtrace_t Backtrace = nullptr; |
232 | |
233 | // The adjusted sample rate for allocation sampling. Default *must* be |
234 | // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++) |
235 | // before GPA::init() is called. This would cause an error in shouldSample(), |
236 | // where we would calculate modulo zero. This value is set UINT32_MAX, as when |
237 | // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating |
238 | // the sample rate. |
239 | uint32_t AdjustedSampleRatePlusOne = 0; |
240 | |
241 | // Additional platform specific data structure for the guarded pool mapping. |
242 | PlatformSpecificMapData GuardedPagePoolPlatformData = {}; |
243 | |
244 | class ScopedRecursiveGuard { |
245 | public: |
246 | ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = true; } |
247 | ~ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = false; } |
248 | }; |
249 | |
250 | // Initialise the PRNG, platform-specific. |
251 | void initPRNG(); |
252 | |
253 | // xorshift (32-bit output), extremely fast PRNG that uses arithmetic |
254 | // operations only. Seeded using platform-specific mechanisms by initPRNG(). |
255 | uint32_t getRandomUnsigned32(); |
256 | }; |
257 | } // namespace gwp_asan |
258 | |
259 | #endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ |
260 | |