]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/compiler-rt/lib/gwp_asan/guarded_pool_allocator.h
Merge llvm, clang, compiler-rt, libc++, libunwind, lld, lldb and openmp
[FreeBSD/FreeBSD.git] / contrib / llvm-project / compiler-rt / lib / gwp_asan / guarded_pool_allocator.h
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/definitions.h"
13 #include "gwp_asan/mutex.h"
14 #include "gwp_asan/options.h"
15 #include "gwp_asan/random.h"
16
17 #include <stddef.h>
18 #include <stdint.h>
19
20 namespace gwp_asan {
21 // This class is the primary implementation of the allocator portion of GWP-
22 // ASan. It is the sole owner of the pool of sequentially allocated guarded
23 // slots. It should always be treated as a singleton.
24
25 // Functions in the public interface of this class are thread-compatible until
26 // init() is called, at which point they become thread-safe (unless specified
27 // otherwise).
28 class GuardedPoolAllocator {
29 public:
30   static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
31
32   enum class Error {
33     UNKNOWN,
34     USE_AFTER_FREE,
35     DOUBLE_FREE,
36     INVALID_FREE,
37     BUFFER_OVERFLOW,
38     BUFFER_UNDERFLOW
39   };
40
41   struct AllocationMetadata {
42     // Maximum number of stack trace frames to collect for allocations + frees.
43     // TODO(hctim): Implement stack frame compression, a-la Chromium.
44     static constexpr size_t kMaximumStackFrames = 64;
45
46     // Records the given allocation metadata into this struct.
47     void RecordAllocation(uintptr_t Addr, size_t Size,
48                           options::Backtrace_t Backtrace);
49
50     // Record that this allocation is now deallocated.
51     void RecordDeallocation(options::Backtrace_t Backtrace);
52
53     struct CallSiteInfo {
54       // The backtrace to the allocation/deallocation. If the first value is
55       // zero, we did not collect a trace.
56       uintptr_t Trace[kMaximumStackFrames] = {};
57       // The thread ID for this trace, or kInvalidThreadID if not available.
58       uint64_t ThreadID = kInvalidThreadID;
59     };
60
61     // The address of this allocation.
62     uintptr_t Addr = 0;
63     // Represents the actual size of the allocation.
64     size_t Size = 0;
65
66     CallSiteInfo AllocationTrace;
67     CallSiteInfo DeallocationTrace;
68
69     // Whether this allocation has been deallocated yet.
70     bool IsDeallocated = false;
71   };
72
73   // During program startup, we must ensure that memory allocations do not land
74   // in this allocation pool if the allocator decides to runtime-disable
75   // GWP-ASan. The constructor value-initialises the class such that if no
76   // further initialisation takes place, calls to shouldSample() and
77   // pointerIsMine() will return false.
78   constexpr GuardedPoolAllocator(){};
79   GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
80   GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
81
82   // Note: This class is expected to be a singleton for the lifetime of the
83   // program. If this object is initialised, it will leak the guarded page pool
84   // and metadata allocations during destruction. We can't clean up these areas
85   // as this may cause a use-after-free on shutdown.
86   ~GuardedPoolAllocator() = default;
87
88   // Initialise the rest of the members of this class. Create the allocation
89   // pool using the provided options. See options.inc for runtime configuration
90   // options.
91   void init(const options::Options &Opts);
92
93   // Return whether the allocation should be randomly chosen for sampling.
94   ALWAYS_INLINE bool shouldSample() {
95     // NextSampleCounter == 0 means we "should regenerate the counter".
96     //                   == 1 means we "should sample this allocation".
97     if (UNLIKELY(ThreadLocals.NextSampleCounter == 0))
98       ThreadLocals.NextSampleCounter =
99           (getRandomUnsigned32() % AdjustedSampleRate) + 1;
100
101     return UNLIKELY(--ThreadLocals.NextSampleCounter == 0);
102   }
103
104   // Returns whether the provided pointer is a current sampled allocation that
105   // is owned by this pool.
106   ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
107     uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
108     return GuardedPagePool <= P && P < GuardedPagePoolEnd;
109   }
110
111   // Allocate memory in a guarded slot, and return a pointer to the new
112   // allocation. Returns nullptr if the pool is empty, the requested size is too
113   // large for this pool to handle, or the requested size is zero.
114   void *allocate(size_t Size);
115
116   // Deallocate memory in a guarded slot. The provided pointer must have been
117   // allocated using this pool. This will set the guarded slot as inaccessible.
118   void deallocate(void *Ptr);
119
120   // Returns the size of the allocation at Ptr.
121   size_t getSize(const void *Ptr);
122
123   // Returns the largest allocation that is supported by this pool. Any
124   // allocations larger than this should go to the regular system allocator.
125   size_t maximumAllocationSize() const;
126
127   // Dumps an error report (including allocation and deallocation stack traces).
128   // An optional error may be provided if the caller knows what the error is
129   // ahead of time. This is primarily a helper function to locate the static
130   // singleton pointer and call the internal version of this function. This
131   // method is never thread safe, and should only be called when fatal errors
132   // occur.
133   static void reportError(uintptr_t AccessPtr, Error E = Error::UNKNOWN);
134
135   // Get the current thread ID, or kInvalidThreadID if failure. Note: This
136   // implementation is platform-specific.
137   static uint64_t getThreadID();
138
139 private:
140   static constexpr size_t kInvalidSlotID = SIZE_MAX;
141
142   // These functions anonymously map memory or change the permissions of mapped
143   // memory into this process in a platform-specific way. Pointer and size
144   // arguments are expected to be page-aligned. These functions will never
145   // return on error, instead electing to kill the calling process on failure.
146   // Note that memory is initially mapped inaccessible. In order for RW
147   // mappings, call mapMemory() followed by markReadWrite() on the returned
148   // pointer.
149   void *mapMemory(size_t Size) const;
150   void markReadWrite(void *Ptr, size_t Size) const;
151   void markInaccessible(void *Ptr, size_t Size) const;
152
153   // Get the page size from the platform-specific implementation. Only needs to
154   // be called once, and the result should be cached in PageSize in this class.
155   static size_t getPlatformPageSize();
156
157   // Install the SIGSEGV crash handler for printing use-after-free and heap-
158   // buffer-{under|over}flow exceptions. This is platform specific as even
159   // though POSIX and Windows both support registering handlers through
160   // signal(), we have to use platform-specific signal handlers to obtain the
161   // address that caused the SIGSEGV exception.
162   static void installSignalHandlers();
163
164   // Returns the index of the slot that this pointer resides in. If the pointer
165   // is not owned by this pool, the result is undefined.
166   size_t addrToSlot(uintptr_t Ptr) const;
167
168   // Returns the address of the N-th guarded slot.
169   uintptr_t slotToAddr(size_t N) const;
170
171   // Returns a pointer to the metadata for the owned pointer. If the pointer is
172   // not owned by this pool, the result is undefined.
173   AllocationMetadata *addrToMetadata(uintptr_t Ptr) const;
174
175   // Returns the address of the page that this pointer resides in.
176   uintptr_t getPageAddr(uintptr_t Ptr) const;
177
178   // Gets the nearest slot to the provided address.
179   size_t getNearestSlot(uintptr_t Ptr) const;
180
181   // Returns whether the provided pointer is a guard page or not. The pointer
182   // must be within memory owned by this pool, else the result is undefined.
183   bool isGuardPage(uintptr_t Ptr) const;
184
185   // Reserve a slot for a new guarded allocation. Returns kInvalidSlotID if no
186   // slot is available to be reserved.
187   size_t reserveSlot();
188
189   // Unreserve the guarded slot.
190   void freeSlot(size_t SlotIndex);
191
192   // Returns the offset (in bytes) between the start of a guarded slot and where
193   // the start of the allocation should take place. Determined using the size of
194   // the allocation and the options provided at init-time.
195   uintptr_t allocationSlotOffset(size_t AllocationSize) const;
196
197   // Returns the diagnosis for an unknown error. If the diagnosis is not
198   // Error::INVALID_FREE or Error::UNKNOWN, the metadata for the slot
199   // responsible for the error is placed in *Meta.
200   Error diagnoseUnknownError(uintptr_t AccessPtr, AllocationMetadata **Meta);
201
202   void reportErrorInternal(uintptr_t AccessPtr, Error E);
203
204   // Cached page size for this system in bytes.
205   size_t PageSize = 0;
206
207   // A mutex to protect the guarded slot and metadata pool for this class.
208   Mutex PoolMutex;
209   // The number of guarded slots that this pool holds.
210   size_t MaxSimultaneousAllocations = 0;
211   // Record the number allocations that we've sampled. We store this amount so
212   // that we don't randomly choose to recycle a slot that previously had an
213   // allocation before all the slots have been utilised.
214   size_t NumSampledAllocations = 0;
215   // Pointer to the pool of guarded slots. Note that this points to the start of
216   // the pool (which is a guard page), not a pointer to the first guarded page.
217   uintptr_t GuardedPagePool = UINTPTR_MAX;
218   uintptr_t GuardedPagePoolEnd = 0;
219   // Pointer to the allocation metadata (allocation/deallocation stack traces),
220   // if any.
221   AllocationMetadata *Metadata = nullptr;
222
223   // Pointer to an array of free slot indexes.
224   size_t *FreeSlots = nullptr;
225   // The current length of the list of free slots.
226   size_t FreeSlotsLength = 0;
227
228   // See options.{h, inc} for more information.
229   bool PerfectlyRightAlign = false;
230
231   // Printf function supplied by the implementing allocator. We can't (in
232   // general) use printf() from the cstdlib as it may malloc(), causing infinite
233   // recursion.
234   options::Printf_t Printf = nullptr;
235   options::Backtrace_t Backtrace = nullptr;
236   options::PrintBacktrace_t PrintBacktrace = nullptr;
237
238   // The adjusted sample rate for allocation sampling. Default *must* be
239   // nonzero, as dynamic initialisation may call malloc (e.g. from libstdc++)
240   // before GPA::init() is called. This would cause an error in shouldSample(),
241   // where we would calculate modulo zero. This value is set UINT32_MAX, as when
242   // GWP-ASan is disabled, we wish to never spend wasted cycles recalculating
243   // the sample rate.
244   uint32_t AdjustedSampleRate = UINT32_MAX;
245
246   // Pack the thread local variables into a struct to ensure that they're in
247   // the same cache line for performance reasons. These are the most touched
248   // variables in GWP-ASan.
249   struct alignas(8) ThreadLocalPackedVariables {
250     constexpr ThreadLocalPackedVariables() {}
251     // Thread-local decrementing counter that indicates that a given allocation
252     // should be sampled when it reaches zero.
253     uint32_t NextSampleCounter = 0;
254     // Guard against recursivity. Unwinders often contain complex behaviour that
255     // may not be safe for the allocator (i.e. the unwinder calls dlopen(),
256     // which calls malloc()). When recursive behaviour is detected, we will
257     // automatically fall back to the supporting allocator to supply the
258     // allocation.
259     bool RecursiveGuard = false;
260   };
261   static TLS_INITIAL_EXEC ThreadLocalPackedVariables ThreadLocals;
262 };
263 } // namespace gwp_asan
264
265 #endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_H_