xref: /llvm-project/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h (revision 680da4b5d7ec05a1e6656f2a7603a7b843268bab)
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