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