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