xref: /openbsd-src/gnu/llvm/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
13cab2bb3Spatrick //===-- guarded_pool_allocator.h --------------------------------*- C++ -*-===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick 
93cab2bb3Spatrick #ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
103cab2bb3Spatrick #define GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
113cab2bb3Spatrick 
121f9cb04fSpatrick #include "gwp_asan/common.h"
133cab2bb3Spatrick #include "gwp_asan/definitions.h"
143cab2bb3Spatrick #include "gwp_asan/mutex.h"
153cab2bb3Spatrick #include "gwp_asan/options.h"
16d89ec533Spatrick #include "gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h" // IWYU pragma: keep
17d89ec533Spatrick #include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h" // IWYU pragma: keep
18d89ec533Spatrick #include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h"
193cab2bb3Spatrick 
203cab2bb3Spatrick #include <stddef.h>
213cab2bb3Spatrick #include <stdint.h>
22d89ec533Spatrick // IWYU pragma: no_include <__stddef_max_align_t.h>
233cab2bb3Spatrick 
243cab2bb3Spatrick namespace gwp_asan {
253cab2bb3Spatrick // This class is the primary implementation of the allocator portion of GWP-
263cab2bb3Spatrick // ASan. It is the sole owner of the pool of sequentially allocated guarded
273cab2bb3Spatrick // slots. It should always be treated as a singleton.
283cab2bb3Spatrick 
293cab2bb3Spatrick // Functions in the public interface of this class are thread-compatible until
303cab2bb3Spatrick // init() is called, at which point they become thread-safe (unless specified
313cab2bb3Spatrick // otherwise).
323cab2bb3Spatrick class GuardedPoolAllocator {
333cab2bb3Spatrick public:
341f9cb04fSpatrick   // Name of the GWP-ASan mapping that for `Metadata`.
351f9cb04fSpatrick   static constexpr const char *kGwpAsanMetadataName = "GWP-ASan Metadata";
363cab2bb3Spatrick 
373cab2bb3Spatrick   // During program startup, we must ensure that memory allocations do not land
383cab2bb3Spatrick   // in this allocation pool if the allocator decides to runtime-disable
393cab2bb3Spatrick   // GWP-ASan. The constructor value-initialises the class such that if no
403cab2bb3Spatrick   // further initialisation takes place, calls to shouldSample() and
413cab2bb3Spatrick   // pointerIsMine() will return false.
GuardedPoolAllocator()42d89ec533Spatrick   constexpr GuardedPoolAllocator() {}
433cab2bb3Spatrick   GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
443cab2bb3Spatrick   GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
453cab2bb3Spatrick 
463cab2bb3Spatrick   // Note: This class is expected to be a singleton for the lifetime of the
473cab2bb3Spatrick   // program. If this object is initialised, it will leak the guarded page pool
483cab2bb3Spatrick   // and metadata allocations during destruction. We can't clean up these areas
493cab2bb3Spatrick   // as this may cause a use-after-free on shutdown.
503cab2bb3Spatrick   ~GuardedPoolAllocator() = default;
513cab2bb3Spatrick 
523cab2bb3Spatrick   // Initialise the rest of the members of this class. Create the allocation
533cab2bb3Spatrick   // pool using the provided options. See options.inc for runtime configuration
543cab2bb3Spatrick   // options.
553cab2bb3Spatrick   void init(const options::Options &Opts);
561f9cb04fSpatrick   void uninitTestOnly();
571f9cb04fSpatrick 
581f9cb04fSpatrick   // Functions exported for libmemunreachable's use on Android. disable()
591f9cb04fSpatrick   // installs a lock in the allocator that prevents any thread from being able
601f9cb04fSpatrick   // to allocate memory, until enable() is called.
611f9cb04fSpatrick   void disable();
621f9cb04fSpatrick   void enable();
631f9cb04fSpatrick 
641f9cb04fSpatrick   typedef void (*iterate_callback)(uintptr_t base, size_t size, void *arg);
651f9cb04fSpatrick   // Execute the callback Cb for every allocation the lies in [Base, Base +
661f9cb04fSpatrick   // Size). Must be called while the allocator is disabled. The callback can not
671f9cb04fSpatrick   // allocate.
681f9cb04fSpatrick   void iterate(void *Base, size_t Size, iterate_callback Cb, void *Arg);
691f9cb04fSpatrick 
703cab2bb3Spatrick   // Return whether the allocation should be randomly chosen for sampling.
shouldSample()713cab2bb3Spatrick   GWP_ASAN_ALWAYS_INLINE bool shouldSample() {
723cab2bb3Spatrick     // NextSampleCounter == 0 means we "should regenerate the counter".
733cab2bb3Spatrick     //                   == 1 means we "should sample this allocation".
741f9cb04fSpatrick     // AdjustedSampleRatePlusOne is designed to intentionally underflow. This
751f9cb04fSpatrick     // class must be valid when zero-initialised, and we wish to sample as
761f9cb04fSpatrick     // infrequently as possible when this is the case, hence we underflow to
771f9cb04fSpatrick     // UINT32_MAX.
78d89ec533Spatrick     if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0))
79d89ec533Spatrick       getThreadLocals()->NextSampleCounter =
80d89ec533Spatrick           ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) &
81d89ec533Spatrick           ThreadLocalPackedVariables::NextSampleCounterMask;
823cab2bb3Spatrick 
83d89ec533Spatrick     return GWP_ASAN_UNLIKELY(--getThreadLocals()->NextSampleCounter == 0);
843cab2bb3Spatrick   }
853cab2bb3Spatrick 
863cab2bb3Spatrick   // Returns whether the provided pointer is a current sampled allocation that
873cab2bb3Spatrick   // is owned by this pool.
pointerIsMine(const void * Ptr)883cab2bb3Spatrick   GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
891f9cb04fSpatrick     return State.pointerIsMine(Ptr);
903cab2bb3Spatrick   }
913cab2bb3Spatrick 
92d89ec533Spatrick   // Allocate memory in a guarded slot, with the specified `Alignment`. Returns
93d89ec533Spatrick   // nullptr if the pool is empty, if the alignnment is not a power of two, or
94d89ec533Spatrick   // if the size/alignment makes the allocation too large for this pool to
95d89ec533Spatrick   // handle. By default, uses strong alignment (i.e. `max_align_t`), see
96d89ec533Spatrick   // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for discussion of
97d89ec533Spatrick   // alignment issues in the standard.
98d89ec533Spatrick   void *allocate(size_t Size, size_t Alignment = alignof(max_align_t));
993cab2bb3Spatrick 
1003cab2bb3Spatrick   // Deallocate memory in a guarded slot. The provided pointer must have been
1013cab2bb3Spatrick   // allocated using this pool. This will set the guarded slot as inaccessible.
1023cab2bb3Spatrick   void deallocate(void *Ptr);
1033cab2bb3Spatrick 
1043cab2bb3Spatrick   // Returns the size of the allocation at Ptr.
1053cab2bb3Spatrick   size_t getSize(const void *Ptr);
1063cab2bb3Spatrick 
1071f9cb04fSpatrick   // Returns a pointer to the Metadata region, or nullptr if it doesn't exist.
getMetadataRegion()1081f9cb04fSpatrick   const AllocationMetadata *getMetadataRegion() const { return Metadata; }
1093cab2bb3Spatrick 
1101f9cb04fSpatrick   // Returns a pointer to the AllocatorState region.
getAllocatorState()1111f9cb04fSpatrick   const AllocatorState *getAllocatorState() const { return &State; }
1123cab2bb3Spatrick 
113*810390e3Srobert   // Functions that the signal handler is responsible for calling, while
114*810390e3Srobert   // providing the SEGV pointer, prior to dumping the crash, and after dumping
115*810390e3Srobert   // the crash (in recoverable mode only).
116*810390e3Srobert   void preCrashReport(void *Ptr);
117*810390e3Srobert   void postCrashReportRecoverableOnly(void *Ptr);
118*810390e3Srobert 
119d89ec533Spatrick   // Exposed as protected for testing.
120d89ec533Spatrick protected:
121d89ec533Spatrick   // Returns the actual allocation size required to service an allocation with
122d89ec533Spatrick   // the provided Size and Alignment.
123d89ec533Spatrick   static size_t getRequiredBackingSize(size_t Size, size_t Alignment,
124d89ec533Spatrick                                        size_t PageSize);
125d89ec533Spatrick 
126d89ec533Spatrick   // Returns the provided pointer that meets the specified alignment, depending
127d89ec533Spatrick   // on whether it's left or right aligned.
128d89ec533Spatrick   static uintptr_t alignUp(uintptr_t Ptr, size_t Alignment);
129d89ec533Spatrick   static uintptr_t alignDown(uintptr_t Ptr, size_t Alignment);
130d89ec533Spatrick 
1313cab2bb3Spatrick private:
1321f9cb04fSpatrick   // Name of actively-occupied slot mappings.
1331f9cb04fSpatrick   static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot";
1341f9cb04fSpatrick   // Name of the guard pages. This includes all slots that are not actively in
1351f9cb04fSpatrick   // use (i.e. were never used, or have been free()'d).)
1361f9cb04fSpatrick   static constexpr const char *kGwpAsanGuardPageName = "GWP-ASan Guard Page";
1371f9cb04fSpatrick   // Name of the mapping for `FreeSlots`.
1381f9cb04fSpatrick   static constexpr const char *kGwpAsanFreeSlotsName = "GWP-ASan Metadata";
1391f9cb04fSpatrick 
1403cab2bb3Spatrick   static constexpr size_t kInvalidSlotID = SIZE_MAX;
1413cab2bb3Spatrick 
1423cab2bb3Spatrick   // These functions anonymously map memory or change the permissions of mapped
1433cab2bb3Spatrick   // memory into this process in a platform-specific way. Pointer and size
1443cab2bb3Spatrick   // arguments are expected to be page-aligned. These functions will never
1453cab2bb3Spatrick   // return on error, instead electing to kill the calling process on failure.
146d89ec533Spatrick   // The pool memory is initially reserved and inaccessible, and RW mappings are
147d89ec533Spatrick   // subsequently created and destroyed via allocateInGuardedPool() and
148d89ec533Spatrick   // deallocateInGuardedPool(). Each mapping is named on platforms that support
149d89ec533Spatrick   // it, primarily Android. This name must be a statically allocated string, as
150d89ec533Spatrick   // the Android kernel uses the string pointer directly.
151d89ec533Spatrick   void *map(size_t Size, const char *Name) const;
152d89ec533Spatrick   void unmap(void *Ptr, size_t Size) const;
153d89ec533Spatrick 
154d89ec533Spatrick   // The pool is managed separately, as some platforms (particularly Fuchsia)
155d89ec533Spatrick   // manage virtual memory regions as a chunk where individual pages can still
156d89ec533Spatrick   // have separate permissions. These platforms maintain metadata about the
157d89ec533Spatrick   // region in order to perform operations. The pool is unique as it's the only
158d89ec533Spatrick   // thing in GWP-ASan that treats pages in a single VM region on an individual
159d89ec533Spatrick   // basis for page protection.
160d89ec533Spatrick   // The pointer returned by reserveGuardedPool() is the reserved address range
161d89ec533Spatrick   // of (at least) Size bytes.
162d89ec533Spatrick   void *reserveGuardedPool(size_t Size);
163d89ec533Spatrick   // allocateInGuardedPool() Ptr and Size must be a subrange of the previously
164d89ec533Spatrick   // reserved pool range.
165d89ec533Spatrick   void allocateInGuardedPool(void *Ptr, size_t Size) const;
166d89ec533Spatrick   // deallocateInGuardedPool() Ptr and Size must be an exact pair previously
167d89ec533Spatrick   // passed to allocateInGuardedPool().
168d89ec533Spatrick   void deallocateInGuardedPool(void *Ptr, size_t Size) const;
169d89ec533Spatrick   void unreserveGuardedPool();
1703cab2bb3Spatrick 
1713cab2bb3Spatrick   // Get the page size from the platform-specific implementation. Only needs to
1723cab2bb3Spatrick   // be called once, and the result should be cached in PageSize in this class.
1733cab2bb3Spatrick   static size_t getPlatformPageSize();
1743cab2bb3Spatrick 
1753cab2bb3Spatrick   // Returns a pointer to the metadata for the owned pointer. If the pointer is
1763cab2bb3Spatrick   // not owned by this pool, the result is undefined.
1773cab2bb3Spatrick   AllocationMetadata *addrToMetadata(uintptr_t Ptr) const;
1783cab2bb3Spatrick 
1793cab2bb3Spatrick   // Reserve a slot for a new guarded allocation. Returns kInvalidSlotID if no
1803cab2bb3Spatrick   // slot is available to be reserved.
1813cab2bb3Spatrick   size_t reserveSlot();
1823cab2bb3Spatrick 
1833cab2bb3Spatrick   // Unreserve the guarded slot.
1843cab2bb3Spatrick   void freeSlot(size_t SlotIndex);
1853cab2bb3Spatrick 
1861f9cb04fSpatrick   // Raise a SEGV and set the corresponding fields in the Allocator's State in
1871f9cb04fSpatrick   // order to tell the crash handler what happened. Used when errors are
1881f9cb04fSpatrick   // detected internally (Double Free, Invalid Free).
189*810390e3Srobert   void raiseInternallyDetectedError(uintptr_t Address, Error E);
1903cab2bb3Spatrick 
1911f9cb04fSpatrick   static GuardedPoolAllocator *getSingleton();
1923cab2bb3Spatrick 
1931f9cb04fSpatrick   // Install a pthread_atfork handler.
1941f9cb04fSpatrick   void installAtFork();
1953cab2bb3Spatrick 
1961f9cb04fSpatrick   gwp_asan::AllocatorState State;
1973cab2bb3Spatrick 
1983cab2bb3Spatrick   // A mutex to protect the guarded slot and metadata pool for this class.
1993cab2bb3Spatrick   Mutex PoolMutex;
200d89ec533Spatrick   // Some unwinders can grab the libdl lock. In order to provide atfork
201d89ec533Spatrick   // protection, we need to ensure that we allow an unwinding thread to release
202d89ec533Spatrick   // the libdl lock before forking.
203d89ec533Spatrick   Mutex BacktraceMutex;
2043cab2bb3Spatrick   // Record the number allocations that we've sampled. We store this amount so
2053cab2bb3Spatrick   // that we don't randomly choose to recycle a slot that previously had an
2063cab2bb3Spatrick   // allocation before all the slots have been utilised.
2073cab2bb3Spatrick   size_t NumSampledAllocations = 0;
2083cab2bb3Spatrick   // Pointer to the allocation metadata (allocation/deallocation stack traces),
2093cab2bb3Spatrick   // if any.
2103cab2bb3Spatrick   AllocationMetadata *Metadata = nullptr;
2113cab2bb3Spatrick 
2123cab2bb3Spatrick   // Pointer to an array of free slot indexes.
2133cab2bb3Spatrick   size_t *FreeSlots = nullptr;
2143cab2bb3Spatrick   // The current length of the list of free slots.
2153cab2bb3Spatrick   size_t FreeSlotsLength = 0;
2163cab2bb3Spatrick 
2173cab2bb3Spatrick   // See options.{h, inc} for more information.
2183cab2bb3Spatrick   bool PerfectlyRightAlign = false;
2193cab2bb3Spatrick 
2201f9cb04fSpatrick   // Backtrace function provided by the supporting allocator. See `options.h`
2211f9cb04fSpatrick   // for more information.
2223cab2bb3Spatrick   options::Backtrace_t Backtrace = nullptr;
2233cab2bb3Spatrick 
2243cab2bb3Spatrick   // The adjusted sample rate for allocation sampling. Default *must* be
2253cab2bb3Spatrick   // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++)
2263cab2bb3Spatrick   // before GPA::init() is called. This would cause an error in shouldSample(),
2273cab2bb3Spatrick   // where we would calculate modulo zero. This value is set UINT32_MAX, as when
2283cab2bb3Spatrick   // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating
2293cab2bb3Spatrick   // the sample rate.
2301f9cb04fSpatrick   uint32_t AdjustedSampleRatePlusOne = 0;
2313cab2bb3Spatrick 
232d89ec533Spatrick   // Additional platform specific data structure for the guarded pool mapping.
233d89ec533Spatrick   PlatformSpecificMapData GuardedPagePoolPlatformData = {};
234d89ec533Spatrick 
235d89ec533Spatrick   class ScopedRecursiveGuard {
236d89ec533Spatrick   public:
ScopedRecursiveGuard()237d89ec533Spatrick     ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = true; }
~ScopedRecursiveGuard()238d89ec533Spatrick     ~ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = false; }
2393cab2bb3Spatrick   };
240d89ec533Spatrick 
241d89ec533Spatrick   // Initialise the PRNG, platform-specific.
242d89ec533Spatrick   void initPRNG();
243d89ec533Spatrick 
244d89ec533Spatrick   // xorshift (32-bit output), extremely fast PRNG that uses arithmetic
245d89ec533Spatrick   // operations only. Seeded using platform-specific mechanisms by initPRNG().
246d89ec533Spatrick   uint32_t getRandomUnsigned32();
2473cab2bb3Spatrick };
2483cab2bb3Spatrick } // namespace gwp_asan
2493cab2bb3Spatrick 
2503cab2bb3Spatrick #endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_
251