1a95edb9dSMitch Phillips //===-- guarded_pool_allocator.h --------------------------------*- C++ -*-===// 2a95edb9dSMitch Phillips // 3a95edb9dSMitch Phillips // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4a95edb9dSMitch Phillips // See https://llvm.org/LICENSE.txt for license information. 5a95edb9dSMitch Phillips // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6a95edb9dSMitch Phillips // 7a95edb9dSMitch Phillips //===----------------------------------------------------------------------===// 8a95edb9dSMitch Phillips 9a95edb9dSMitch Phillips #ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ 10a95edb9dSMitch Phillips #define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ 11a95edb9dSMitch Phillips 12a6258684SMitch Phillips #include "gwp_asan/common.h" 13a95edb9dSMitch Phillips #include "gwp_asan/definitions.h" 14a95edb9dSMitch Phillips #include "gwp_asan/mutex.h" 15a95edb9dSMitch Phillips #include "gwp_asan/options.h" 1661a038f8SMitch Phillips #include "gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h" // IWYU pragma: keep 1761a038f8SMitch Phillips #include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h" // IWYU pragma: keep 1890678f65SKostya Kortchinsky #include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h" 19a95edb9dSMitch Phillips 20a95edb9dSMitch Phillips #include <stddef.h> 21a95edb9dSMitch Phillips #include <stdint.h> 223d8823b8SMitch Phillips // IWYU pragma: no_include <__stddef_max_align_t.h> 23*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_null.h> 24*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_nullptr_t.h> 25*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_offsetof.h> 26*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_ptrdiff_t.h> 27*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_rsize_t.h> 28*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_size_t.h> 29*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_unreachable.h> 30*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_wchar_t.h> 31*680da4b5SIan Anderson // IWYU pragma: no_include <__stddef_wint_t.h> 32a95edb9dSMitch Phillips 33a95edb9dSMitch Phillips namespace gwp_asan { 34a95edb9dSMitch Phillips // This class is the primary implementation of the allocator portion of GWP- 35a95edb9dSMitch Phillips // ASan. It is the sole owner of the pool of sequentially allocated guarded 36a95edb9dSMitch Phillips // slots. It should always be treated as a singleton. 37a95edb9dSMitch Phillips 38a95edb9dSMitch Phillips // Functions in the public interface of this class are thread-compatible until 39a95edb9dSMitch Phillips // init() is called, at which point they become thread-safe (unless specified 40a95edb9dSMitch Phillips // otherwise). 41a95edb9dSMitch Phillips class GuardedPoolAllocator { 42a95edb9dSMitch Phillips public: 43e1440f59SMitch Phillips // Name of the GWP-ASan mapping that for `Metadata`. 44e1440f59SMitch Phillips static constexpr const char *kGwpAsanMetadataName = "GWP-ASan Metadata"; 45e1440f59SMitch Phillips 46a95edb9dSMitch Phillips // During program startup, we must ensure that memory allocations do not land 47a95edb9dSMitch Phillips // in this allocation pool if the allocator decides to runtime-disable 48a95edb9dSMitch Phillips // GWP-ASan. The constructor value-initialises the class such that if no 49a95edb9dSMitch Phillips // further initialisation takes place, calls to shouldSample() and 50a95edb9dSMitch Phillips // pointerIsMine() will return false. GuardedPoolAllocator()51ae9d0400SKostya Kortchinsky constexpr GuardedPoolAllocator() {} 52a95edb9dSMitch Phillips GuardedPoolAllocator(const GuardedPoolAllocator &) = delete; 53a95edb9dSMitch Phillips GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete; 54a95edb9dSMitch Phillips 55a95edb9dSMitch Phillips // Note: This class is expected to be a singleton for the lifetime of the 56a95edb9dSMitch Phillips // program. If this object is initialised, it will leak the guarded page pool 57a95edb9dSMitch Phillips // and metadata allocations during destruction. We can't clean up these areas 58a95edb9dSMitch Phillips // as this may cause a use-after-free on shutdown. 59a95edb9dSMitch Phillips ~GuardedPoolAllocator() = default; 60a95edb9dSMitch Phillips 61a95edb9dSMitch Phillips // Initialise the rest of the members of this class. Create the allocation 62a95edb9dSMitch Phillips // pool using the provided options. See options.inc for runtime configuration 63a95edb9dSMitch Phillips // options. 64a95edb9dSMitch Phillips void init(const options::Options &Opts); 65596d0614SEvgenii Stepanov void uninitTestOnly(); 66596d0614SEvgenii Stepanov 67a6258684SMitch Phillips // Functions exported for libmemunreachable's use on Android. disable() 68a6258684SMitch Phillips // installs a lock in the allocator that prevents any thread from being able 69a6258684SMitch Phillips // to allocate memory, until enable() is called. 70596d0614SEvgenii Stepanov void disable(); 71596d0614SEvgenii Stepanov void enable(); 72a95edb9dSMitch Phillips 7346044a69SEvgenii Stepanov typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg); 74a6258684SMitch Phillips // Execute the callback Cb for every allocation the lies in [Base, Base + 75a6258684SMitch Phillips // Size). Must be called while the allocator is disabled. The callback can not 7646044a69SEvgenii Stepanov // allocate. 7746044a69SEvgenii Stepanov void iterate(void *Base, size_t Size, iterate_callback Cb, void *Arg); 7846044a69SEvgenii Stepanov 79a95edb9dSMitch Phillips // Return whether the allocation should be randomly chosen for sampling. shouldSample()8019edfb37SMitch Phillips GWP_ASAN_ALWAYS_INLINE bool shouldSample() { 81a95edb9dSMitch Phillips // NextSampleCounter == 0 means we "should regenerate the counter". 82a95edb9dSMitch Phillips // == 1 means we "should sample this allocation". 83596d0614SEvgenii Stepanov // AdjustedSampleRatePlusOne is designed to intentionally underflow. This 84596d0614SEvgenii Stepanov // class must be valid when zero-initialised, and we wish to sample as 85596d0614SEvgenii Stepanov // infrequently as possible when this is the case, hence we underflow to 86596d0614SEvgenii Stepanov // UINT32_MAX. 8790678f65SKostya Kortchinsky if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0)) 8890678f65SKostya Kortchinsky getThreadLocals()->NextSampleCounter = 893580a450SKostya Kortchinsky ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) & 903580a450SKostya Kortchinsky ThreadLocalPackedVariables::NextSampleCounterMask; 91a95edb9dSMitch Phillips 9290678f65SKostya Kortchinsky return GWP_ASAN_UNLIKELY(--getThreadLocals()->NextSampleCounter == 0); 93a95edb9dSMitch Phillips } 94a95edb9dSMitch Phillips 95a95edb9dSMitch Phillips // Returns whether the provided pointer is a current sampled allocation that 96a95edb9dSMitch Phillips // is owned by this pool. pointerIsMine(const void * Ptr)9719edfb37SMitch Phillips GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const { 98a6258684SMitch Phillips return State.pointerIsMine(Ptr); 99a95edb9dSMitch Phillips } 100a95edb9dSMitch Phillips 1013d8823b8SMitch Phillips // Allocate memory in a guarded slot, with the specified `Alignment`. Returns 1023d8823b8SMitch Phillips // nullptr if the pool is empty, if the alignnment is not a power of two, or 1033d8823b8SMitch Phillips // if the size/alignment makes the allocation too large for this pool to 1043d8823b8SMitch Phillips // handle. By default, uses strong alignment (i.e. `max_align_t`), see 1053d8823b8SMitch Phillips // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for discussion of 1063d8823b8SMitch Phillips // alignment issues in the standard. 1073d8823b8SMitch Phillips void *allocate(size_t Size, size_t Alignment = alignof(max_align_t)); 108a95edb9dSMitch Phillips 109a95edb9dSMitch Phillips // Deallocate memory in a guarded slot. The provided pointer must have been 110a95edb9dSMitch Phillips // allocated using this pool. This will set the guarded slot as inaccessible. 111a95edb9dSMitch Phillips void deallocate(void *Ptr); 112a95edb9dSMitch Phillips 113a95edb9dSMitch Phillips // Returns the size of the allocation at Ptr. 114a95edb9dSMitch Phillips size_t getSize(const void *Ptr); 115a95edb9dSMitch Phillips 116a6258684SMitch Phillips // Returns a pointer to the Metadata region, or nullptr if it doesn't exist. getMetadataRegion()117a6258684SMitch Phillips const AllocationMetadata *getMetadataRegion() const { return Metadata; } 118a95edb9dSMitch Phillips 119a6258684SMitch Phillips // Returns a pointer to the AllocatorState region. getAllocatorState()120a6258684SMitch Phillips const AllocatorState *getAllocatorState() const { return &State; } 121b157dcacSMitch Phillips 12235b5499dSMitch Phillips // Functions that the signal handler is responsible for calling, while 12335b5499dSMitch Phillips // providing the SEGV pointer, prior to dumping the crash, and after dumping 12435b5499dSMitch Phillips // the crash (in recoverable mode only). 12535b5499dSMitch Phillips void preCrashReport(void *Ptr); 12635b5499dSMitch Phillips void postCrashReportRecoverableOnly(void *Ptr); 12735b5499dSMitch Phillips 1283d8823b8SMitch Phillips // Exposed as protected for testing. 1293d8823b8SMitch Phillips protected: 1303d8823b8SMitch Phillips // Returns the actual allocation size required to service an allocation with 1313d8823b8SMitch Phillips // the provided Size and Alignment. 1323d8823b8SMitch Phillips static size_t getRequiredBackingSize(size_t Size, size_t Alignment, 1333d8823b8SMitch Phillips size_t PageSize); 1343d8823b8SMitch Phillips 1353d8823b8SMitch Phillips // Returns the provided pointer that meets the specified alignment, depending 1363d8823b8SMitch Phillips // on whether it's left or right aligned. 1373d8823b8SMitch Phillips static uintptr_t alignUp(uintptr_t Ptr, size_t Alignment); 1383d8823b8SMitch Phillips static uintptr_t alignDown(uintptr_t Ptr, size_t Alignment); 1393d8823b8SMitch Phillips 140a95edb9dSMitch Phillips private: 141e1440f59SMitch Phillips // Name of actively-occupied slot mappings. 142e1440f59SMitch Phillips static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot"; 143e1440f59SMitch Phillips // Name of the guard pages. This includes all slots that are not actively in 144e1440f59SMitch Phillips // use (i.e. were never used, or have been free()'d).) 145e1440f59SMitch Phillips static constexpr const char *kGwpAsanGuardPageName = "GWP-ASan Guard Page"; 146e1440f59SMitch Phillips // Name of the mapping for `FreeSlots`. 147e1440f59SMitch Phillips static constexpr const char *kGwpAsanFreeSlotsName = "GWP-ASan Metadata"; 148e1440f59SMitch Phillips 149a95edb9dSMitch Phillips static constexpr size_t kInvalidSlotID = SIZE_MAX; 150a95edb9dSMitch Phillips 151a95edb9dSMitch Phillips // These functions anonymously map memory or change the permissions of mapped 152a95edb9dSMitch Phillips // memory into this process in a platform-specific way. Pointer and size 153a95edb9dSMitch Phillips // arguments are expected to be page-aligned. These functions will never 154a95edb9dSMitch Phillips // return on error, instead electing to kill the calling process on failure. 155612e02eeSKostya Kortchinsky // The pool memory is initially reserved and inaccessible, and RW mappings are 156612e02eeSKostya Kortchinsky // subsequently created and destroyed via allocateInGuardedPool() and 157612e02eeSKostya Kortchinsky // deallocateInGuardedPool(). Each mapping is named on platforms that support 158612e02eeSKostya Kortchinsky // it, primarily Android. This name must be a statically allocated string, as 159612e02eeSKostya Kortchinsky // the Android kernel uses the string pointer directly. 160612e02eeSKostya Kortchinsky void *map(size_t Size, const char *Name) const; 161612e02eeSKostya Kortchinsky void unmap(void *Ptr, size_t Size) const; 162612e02eeSKostya Kortchinsky 163612e02eeSKostya Kortchinsky // The pool is managed separately, as some platforms (particularly Fuchsia) 164612e02eeSKostya Kortchinsky // manage virtual memory regions as a chunk where individual pages can still 165612e02eeSKostya Kortchinsky // have separate permissions. These platforms maintain metadata about the 166612e02eeSKostya Kortchinsky // region in order to perform operations. The pool is unique as it's the only 167612e02eeSKostya Kortchinsky // thing in GWP-ASan that treats pages in a single VM region on an individual 168612e02eeSKostya Kortchinsky // basis for page protection. 169612e02eeSKostya Kortchinsky // The pointer returned by reserveGuardedPool() is the reserved address range 170612e02eeSKostya Kortchinsky // of (at least) Size bytes. 171612e02eeSKostya Kortchinsky void *reserveGuardedPool(size_t Size); 172612e02eeSKostya Kortchinsky // allocateInGuardedPool() Ptr and Size must be a subrange of the previously 173612e02eeSKostya Kortchinsky // reserved pool range. 174612e02eeSKostya Kortchinsky void allocateInGuardedPool(void *Ptr, size_t Size) const; 175612e02eeSKostya Kortchinsky // deallocateInGuardedPool() Ptr and Size must be an exact pair previously 176612e02eeSKostya Kortchinsky // passed to allocateInGuardedPool(). 177612e02eeSKostya Kortchinsky void deallocateInGuardedPool(void *Ptr, size_t Size) const; 178612e02eeSKostya Kortchinsky void unreserveGuardedPool(); 179a95edb9dSMitch Phillips 180a95edb9dSMitch Phillips // Get the page size from the platform-specific implementation. Only needs to 181a95edb9dSMitch Phillips // be called once, and the result should be cached in PageSize in this class. 182a95edb9dSMitch Phillips static size_t getPlatformPageSize(); 183a95edb9dSMitch Phillips 184a95edb9dSMitch Phillips // Returns a pointer to the metadata for the owned pointer. If the pointer is 185a95edb9dSMitch Phillips // not owned by this pool, the result is undefined. 186a95edb9dSMitch Phillips AllocationMetadata *addrToMetadata(uintptr_t Ptr) const; 187a95edb9dSMitch Phillips 188a95edb9dSMitch Phillips // Reserve a slot for a new guarded allocation. Returns kInvalidSlotID if no 189a95edb9dSMitch Phillips // slot is available to be reserved. 190a95edb9dSMitch Phillips size_t reserveSlot(); 191a95edb9dSMitch Phillips 192a95edb9dSMitch Phillips // Unreserve the guarded slot. 193a95edb9dSMitch Phillips void freeSlot(size_t SlotIndex); 194a95edb9dSMitch Phillips 195a6258684SMitch Phillips // Raise a SEGV and set the corresponding fields in the Allocator's State in 196a6258684SMitch Phillips // order to tell the crash handler what happened. Used when errors are 197a6258684SMitch Phillips // detected internally (Double Free, Invalid Free). 19835b5499dSMitch Phillips void raiseInternallyDetectedError(uintptr_t Address, Error E); 199a95edb9dSMitch Phillips 200596d0614SEvgenii Stepanov static GuardedPoolAllocator *getSingleton(); 201596d0614SEvgenii Stepanov 202596d0614SEvgenii Stepanov // Install a pthread_atfork handler. 203596d0614SEvgenii Stepanov void installAtFork(); 204596d0614SEvgenii Stepanov 205a6258684SMitch Phillips gwp_asan::AllocatorState State; 206a95edb9dSMitch Phillips 207a95edb9dSMitch Phillips // A mutex to protect the guarded slot and metadata pool for this class. 208a95edb9dSMitch Phillips Mutex PoolMutex; 20930973f6fSMitch Phillips // Some unwinders can grab the libdl lock. In order to provide atfork 21030973f6fSMitch Phillips // protection, we need to ensure that we allow an unwinding thread to release 21130973f6fSMitch Phillips // the libdl lock before forking. 21230973f6fSMitch Phillips Mutex BacktraceMutex; 213a95edb9dSMitch Phillips // Record the number allocations that we've sampled. We store this amount so 214a95edb9dSMitch Phillips // that we don't randomly choose to recycle a slot that previously had an 215a95edb9dSMitch Phillips // allocation before all the slots have been utilised. 216a95edb9dSMitch Phillips size_t NumSampledAllocations = 0; 217a95edb9dSMitch Phillips // Pointer to the allocation metadata (allocation/deallocation stack traces), 218a95edb9dSMitch Phillips // if any. 219a95edb9dSMitch Phillips AllocationMetadata *Metadata = nullptr; 220a95edb9dSMitch Phillips 221a95edb9dSMitch Phillips // Pointer to an array of free slot indexes. 222a95edb9dSMitch Phillips size_t *FreeSlots = nullptr; 223a95edb9dSMitch Phillips // The current length of the list of free slots. 224a95edb9dSMitch Phillips size_t FreeSlotsLength = 0; 225a95edb9dSMitch Phillips 226a95edb9dSMitch Phillips // See options.{h, inc} for more information. 227a95edb9dSMitch Phillips bool PerfectlyRightAlign = false; 228a95edb9dSMitch Phillips 229a6258684SMitch Phillips // Backtrace function provided by the supporting allocator. See `options.h` 230a6258684SMitch Phillips // for more information. 2317339ca27SMitch Phillips options::Backtrace_t Backtrace = nullptr; 232a95edb9dSMitch Phillips 233a95edb9dSMitch Phillips // The adjusted sample rate for allocation sampling. Default *must* be 234a95edb9dSMitch Phillips // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++) 235a95edb9dSMitch Phillips // before GPA::init() is called. This would cause an error in shouldSample(), 236a95edb9dSMitch Phillips // where we would calculate modulo zero. This value is set UINT32_MAX, as when 237a95edb9dSMitch Phillips // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating 238a95edb9dSMitch Phillips // the sample rate. 239596d0614SEvgenii Stepanov uint32_t AdjustedSampleRatePlusOne = 0; 24005d1a2bdSMitch Phillips 24163ad0876SKostya Kortchinsky // Additional platform specific data structure for the guarded pool mapping. 24263ad0876SKostya Kortchinsky PlatformSpecificMapData GuardedPagePoolPlatformData = {}; 24363ad0876SKostya Kortchinsky 2443580a450SKostya Kortchinsky class ScopedRecursiveGuard { 2453580a450SKostya Kortchinsky public: ScopedRecursiveGuard()24690678f65SKostya Kortchinsky ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = true; } ~ScopedRecursiveGuard()24790678f65SKostya Kortchinsky ~ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = false; } 2483580a450SKostya Kortchinsky }; 2493580a450SKostya Kortchinsky 2503580a450SKostya Kortchinsky // Initialise the PRNG, platform-specific. 2513580a450SKostya Kortchinsky void initPRNG(); 2523580a450SKostya Kortchinsky 2533580a450SKostya Kortchinsky // xorshift (32-bit output), extremely fast PRNG that uses arithmetic 2543580a450SKostya Kortchinsky // operations only. Seeded using platform-specific mechanisms by initPRNG(). 2553580a450SKostya Kortchinsky uint32_t getRandomUnsigned32(); 256a95edb9dSMitch Phillips }; 257a95edb9dSMitch Phillips } // namespace gwp_asan 258a95edb9dSMitch Phillips 259a95edb9dSMitch Phillips #endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_ 260