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