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