1 | //===-- guarded_pool_allocator.cpp ------------------------------*- 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 | #include "gwp_asan/guarded_pool_allocator.h" |
10 | |
11 | #include "gwp_asan/crash_handler.h" |
12 | #include "gwp_asan/options.h" |
13 | #include "gwp_asan/utilities.h" |
14 | |
15 | #include <assert.h> |
16 | #include <stddef.h> |
17 | |
18 | using AllocationMetadata = gwp_asan::AllocationMetadata; |
19 | using Error = gwp_asan::Error; |
20 | |
21 | namespace gwp_asan { |
22 | namespace { |
23 | // Forward declare the pointer to the singleton version of this class. |
24 | // Instantiated during initialisation, this allows the signal handler |
25 | // to find this class in order to deduce the root cause of failures. Must not be |
26 | // referenced by users outside this translation unit, in order to avoid |
27 | // init-order-fiasco. |
28 | GuardedPoolAllocator *SingletonPtr = nullptr; |
29 | |
30 | size_t roundUpTo(size_t Size, size_t Boundary) { |
31 | return (Size + Boundary - 1) & ~(Boundary - 1); |
32 | } |
33 | |
34 | uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) { |
35 | return Ptr & ~(PageSize - 1); |
36 | } |
37 | |
38 | bool isPowerOfTwo(uintptr_t X) { return (X & (X - 1)) == 0; } |
39 | } // anonymous namespace |
40 | |
41 | // Gets the singleton implementation of this class. Thread-compatible until |
42 | // init() is called, thread-safe afterwards. |
43 | GuardedPoolAllocator *GuardedPoolAllocator::getSingleton() { |
44 | return SingletonPtr; |
45 | } |
46 | |
47 | void GuardedPoolAllocator::init(const options::Options &Opts) { |
48 | // Note: We return from the constructor here if GWP-ASan is not available. |
49 | // This will stop heap-allocation of class members, as well as mmap() of the |
50 | // guarded slots. |
51 | if (!Opts.Enabled || Opts.SampleRate == 0 || |
52 | Opts.MaxSimultaneousAllocations == 0) |
53 | return; |
54 | |
55 | check(Condition: Opts.SampleRate >= 0, Message: "GWP-ASan Error: SampleRate is < 0." ); |
56 | check(Condition: Opts.SampleRate < (1 << 30), Message: "GWP-ASan Error: SampleRate is >= 2^30." ); |
57 | check(Condition: Opts.MaxSimultaneousAllocations >= 0, |
58 | Message: "GWP-ASan Error: MaxSimultaneousAllocations is < 0." ); |
59 | |
60 | check(Condition: SingletonPtr == nullptr, |
61 | Message: "There's already a live GuardedPoolAllocator!" ); |
62 | SingletonPtr = this; |
63 | Backtrace = Opts.Backtrace; |
64 | |
65 | State.VersionMagic = {.Magic: {AllocatorVersionMagic::kAllocatorVersionMagic[0], |
66 | AllocatorVersionMagic::kAllocatorVersionMagic[1], |
67 | AllocatorVersionMagic::kAllocatorVersionMagic[2], |
68 | AllocatorVersionMagic::kAllocatorVersionMagic[3]}, |
69 | .Version: AllocatorVersionMagic::kAllocatorVersion, |
70 | .Reserved: 0}; |
71 | |
72 | State.MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations; |
73 | |
74 | const size_t PageSize = getPlatformPageSize(); |
75 | // getPageAddr() and roundUpTo() assume the page size to be a power of 2. |
76 | assert((PageSize & (PageSize - 1)) == 0); |
77 | State.PageSize = PageSize; |
78 | |
79 | // Number of pages required = |
80 | // + MaxSimultaneousAllocations * maximumAllocationSize (N pages per slot) |
81 | // + MaxSimultaneousAllocations (one guard on the left side of each slot) |
82 | // + 1 (an extra guard page at the end of the pool, on the right side) |
83 | // + 1 (an extra page that's used for reporting internally-detected crashes, |
84 | // like double free and invalid free, to the signal handler; see |
85 | // raiseInternallyDetectedError() for more info) |
86 | size_t PoolBytesRequired = |
87 | PageSize * (2 + State.MaxSimultaneousAllocations) + |
88 | State.MaxSimultaneousAllocations * State.maximumAllocationSize(); |
89 | assert(PoolBytesRequired % PageSize == 0); |
90 | void *GuardedPoolMemory = reserveGuardedPool(Size: PoolBytesRequired); |
91 | |
92 | size_t BytesRequired = |
93 | roundUpTo(Size: State.MaxSimultaneousAllocations * sizeof(*Metadata), Boundary: PageSize); |
94 | Metadata = reinterpret_cast<AllocationMetadata *>( |
95 | map(Size: BytesRequired, Name: kGwpAsanMetadataName)); |
96 | |
97 | // Allocate memory and set up the free pages queue. |
98 | BytesRequired = roundUpTo( |
99 | Size: State.MaxSimultaneousAllocations * sizeof(*FreeSlots), Boundary: PageSize); |
100 | FreeSlots = |
101 | reinterpret_cast<size_t *>(map(Size: BytesRequired, Name: kGwpAsanFreeSlotsName)); |
102 | |
103 | // Multiply the sample rate by 2 to give a good, fast approximation for (1 / |
104 | // SampleRate) chance of sampling. |
105 | if (Opts.SampleRate != 1) |
106 | AdjustedSampleRatePlusOne = static_cast<uint32_t>(Opts.SampleRate) * 2 + 1; |
107 | else |
108 | AdjustedSampleRatePlusOne = 2; |
109 | |
110 | initPRNG(); |
111 | getThreadLocals()->NextSampleCounter = |
112 | ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) & |
113 | ThreadLocalPackedVariables::NextSampleCounterMask; |
114 | |
115 | State.GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory); |
116 | State.GuardedPagePoolEnd = |
117 | reinterpret_cast<uintptr_t>(GuardedPoolMemory) + PoolBytesRequired; |
118 | |
119 | if (Opts.InstallForkHandlers) |
120 | installAtFork(); |
121 | } |
122 | |
123 | void GuardedPoolAllocator::disable() { |
124 | PoolMutex.lock(); |
125 | BacktraceMutex.lock(); |
126 | } |
127 | |
128 | void GuardedPoolAllocator::enable() { |
129 | PoolMutex.unlock(); |
130 | BacktraceMutex.unlock(); |
131 | } |
132 | |
133 | void GuardedPoolAllocator::iterate(void *Base, size_t Size, iterate_callback Cb, |
134 | void *Arg) { |
135 | uintptr_t Start = reinterpret_cast<uintptr_t>(Base); |
136 | for (size_t i = 0; i < State.MaxSimultaneousAllocations; ++i) { |
137 | const AllocationMetadata &Meta = Metadata[i]; |
138 | if (Meta.Addr && !Meta.IsDeallocated && Meta.Addr >= Start && |
139 | Meta.Addr < Start + Size) |
140 | Cb(Meta.Addr, Meta.RequestedSize, Arg); |
141 | } |
142 | } |
143 | |
144 | void GuardedPoolAllocator::uninitTestOnly() { |
145 | if (State.GuardedPagePool) { |
146 | unreserveGuardedPool(); |
147 | State.GuardedPagePool = 0; |
148 | State.GuardedPagePoolEnd = 0; |
149 | } |
150 | if (Metadata) { |
151 | unmap(Ptr: Metadata, |
152 | Size: roundUpTo(Size: State.MaxSimultaneousAllocations * sizeof(*Metadata), |
153 | Boundary: State.PageSize)); |
154 | Metadata = nullptr; |
155 | } |
156 | if (FreeSlots) { |
157 | unmap(Ptr: FreeSlots, |
158 | Size: roundUpTo(Size: State.MaxSimultaneousAllocations * sizeof(*FreeSlots), |
159 | Boundary: State.PageSize)); |
160 | FreeSlots = nullptr; |
161 | } |
162 | *getThreadLocals() = ThreadLocalPackedVariables(); |
163 | SingletonPtr = nullptr; |
164 | } |
165 | |
166 | // Note, minimum backing allocation size in GWP-ASan is always one page, and |
167 | // each slot could potentially be multiple pages (but always in |
168 | // page-increments). Thus, for anything that requires less than page size |
169 | // alignment, we don't need to allocate extra padding to ensure the alignment |
170 | // can be met. |
171 | size_t GuardedPoolAllocator::getRequiredBackingSize(size_t Size, |
172 | size_t Alignment, |
173 | size_t PageSize) { |
174 | assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!" ); |
175 | assert(Alignment != 0 && "Alignment should be non-zero" ); |
176 | assert(Size != 0 && "Size should be non-zero" ); |
177 | |
178 | if (Alignment <= PageSize) |
179 | return Size; |
180 | |
181 | return Size + Alignment - PageSize; |
182 | } |
183 | |
184 | uintptr_t GuardedPoolAllocator::alignUp(uintptr_t Ptr, size_t Alignment) { |
185 | assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!" ); |
186 | assert(Alignment != 0 && "Alignment should be non-zero" ); |
187 | if ((Ptr & (Alignment - 1)) == 0) |
188 | return Ptr; |
189 | |
190 | Ptr += Alignment - (Ptr & (Alignment - 1)); |
191 | return Ptr; |
192 | } |
193 | |
194 | uintptr_t GuardedPoolAllocator::alignDown(uintptr_t Ptr, size_t Alignment) { |
195 | assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!" ); |
196 | assert(Alignment != 0 && "Alignment should be non-zero" ); |
197 | if ((Ptr & (Alignment - 1)) == 0) |
198 | return Ptr; |
199 | |
200 | Ptr -= Ptr & (Alignment - 1); |
201 | return Ptr; |
202 | } |
203 | |
204 | void *GuardedPoolAllocator::allocate(size_t Size, size_t Alignment) { |
205 | // GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall |
206 | // back to the supporting allocator. |
207 | if (State.GuardedPagePoolEnd == 0) { |
208 | getThreadLocals()->NextSampleCounter = |
209 | (AdjustedSampleRatePlusOne - 1) & |
210 | ThreadLocalPackedVariables::NextSampleCounterMask; |
211 | return nullptr; |
212 | } |
213 | |
214 | if (Size == 0) |
215 | Size = 1; |
216 | if (Alignment == 0) |
217 | Alignment = alignof(max_align_t); |
218 | |
219 | if (!isPowerOfTwo(X: Alignment) || Alignment > State.maximumAllocationSize() || |
220 | Size > State.maximumAllocationSize()) |
221 | return nullptr; |
222 | |
223 | size_t BackingSize = getRequiredBackingSize(Size, Alignment, PageSize: State.PageSize); |
224 | if (BackingSize > State.maximumAllocationSize()) |
225 | return nullptr; |
226 | |
227 | // Protect against recursivity. |
228 | if (getThreadLocals()->RecursiveGuard) |
229 | return nullptr; |
230 | ScopedRecursiveGuard SRG; |
231 | |
232 | size_t Index; |
233 | { |
234 | ScopedLock L(PoolMutex); |
235 | Index = reserveSlot(); |
236 | } |
237 | |
238 | if (Index == kInvalidSlotID) |
239 | return nullptr; |
240 | |
241 | uintptr_t SlotStart = State.slotToAddr(N: Index); |
242 | AllocationMetadata *Meta = addrToMetadata(Ptr: SlotStart); |
243 | uintptr_t SlotEnd = State.slotToAddr(N: Index) + State.maximumAllocationSize(); |
244 | uintptr_t UserPtr; |
245 | // Randomly choose whether to left-align or right-align the allocation, and |
246 | // then apply the necessary adjustments to get an aligned pointer. |
247 | if (getRandomUnsigned32() % 2 == 0) |
248 | UserPtr = alignUp(Ptr: SlotStart, Alignment); |
249 | else |
250 | UserPtr = alignDown(Ptr: SlotEnd - Size, Alignment); |
251 | |
252 | assert(UserPtr >= SlotStart); |
253 | assert(UserPtr + Size <= SlotEnd); |
254 | |
255 | // If a slot is multiple pages in size, and the allocation takes up a single |
256 | // page, we can improve overflow detection by leaving the unused pages as |
257 | // unmapped. |
258 | const size_t PageSize = State.PageSize; |
259 | allocateInGuardedPool( |
260 | Ptr: reinterpret_cast<void *>(getPageAddr(Ptr: UserPtr, PageSize)), |
261 | Size: roundUpTo(Size, Boundary: PageSize)); |
262 | |
263 | Meta->RecordAllocation(Addr: UserPtr, RequestedSize: Size); |
264 | { |
265 | ScopedLock UL(BacktraceMutex); |
266 | Meta->AllocationTrace.RecordBacktrace(Backtrace); |
267 | } |
268 | |
269 | return reinterpret_cast<void *>(UserPtr); |
270 | } |
271 | |
272 | void GuardedPoolAllocator::raiseInternallyDetectedError(uintptr_t Address, |
273 | Error E) { |
274 | // Disable the allocator before setting the internal failure state. In |
275 | // non-recoverable mode, the allocator will be permanently disabled, and so |
276 | // things will be accessed without locks. |
277 | disable(); |
278 | |
279 | // Races between internally- and externally-raised faults can happen. Right |
280 | // now, in this thread we've locked the allocator in order to raise an |
281 | // internally-detected fault, and another thread could SIGSEGV to raise an |
282 | // externally-detected fault. What will happen is that the other thread will |
283 | // wait in the signal handler, as we hold the allocator's locks from the |
284 | // disable() above. We'll trigger the signal handler by touching the |
285 | // internal-signal-raising address below, and the signal handler from our |
286 | // thread will get to run first as we will continue to hold the allocator |
287 | // locks until the enable() at the end of this function. Be careful though, if |
288 | // this thread receives another SIGSEGV after the disable() above, but before |
289 | // touching the internal-signal-raising address below, then this thread will |
290 | // get an "externally-raised" SIGSEGV while *also* holding the allocator |
291 | // locks, which means this thread's signal handler will deadlock. This could |
292 | // be resolved with a re-entrant lock, but asking platforms to implement this |
293 | // seems unnecessary given the only way to get a SIGSEGV in this critical |
294 | // section is either a memory safety bug in the couple lines of code below (be |
295 | // careful!), or someone outside uses `kill(this_thread, SIGSEGV)`, which |
296 | // really shouldn't happen. |
297 | |
298 | State.FailureType = E; |
299 | State.FailureAddress = Address; |
300 | |
301 | // Raise a SEGV by touching a specific address that identifies to the crash |
302 | // handler that this is an internally-raised fault. Changing this address? |
303 | // Don't forget to update __gwp_asan_get_internal_crash_address. |
304 | volatile char *p = |
305 | reinterpret_cast<char *>(State.internallyDetectedErrorFaultAddress()); |
306 | *p = 0; |
307 | |
308 | // This should never be reached in non-recoverable mode. Ensure that the |
309 | // signal handler called handleRecoverablePostCrashReport(), which was |
310 | // responsible for re-setting these fields. |
311 | assert(State.FailureType == Error::UNKNOWN); |
312 | assert(State.FailureAddress == 0u); |
313 | |
314 | // In recoverable mode, the signal handler (after dumping the crash) marked |
315 | // the page containing the InternalFaultSegvAddress as read/writeable, to |
316 | // allow the second touch to succeed after returning from the signal handler. |
317 | // Now, we need to mark the page as non-read/write-able again, so future |
318 | // internal faults can be raised. |
319 | deallocateInGuardedPool( |
320 | Ptr: reinterpret_cast<void *>(getPageAddr( |
321 | Ptr: State.internallyDetectedErrorFaultAddress(), PageSize: State.PageSize)), |
322 | Size: State.PageSize); |
323 | |
324 | // And now we're done with patching ourselves back up, enable the allocator. |
325 | enable(); |
326 | } |
327 | |
328 | void GuardedPoolAllocator::deallocate(void *Ptr) { |
329 | assert(pointerIsMine(Ptr) && "Pointer is not mine!" ); |
330 | uintptr_t UPtr = reinterpret_cast<uintptr_t>(Ptr); |
331 | size_t Slot = State.getNearestSlot(Ptr: UPtr); |
332 | uintptr_t SlotStart = State.slotToAddr(N: Slot); |
333 | AllocationMetadata *Meta = addrToMetadata(Ptr: UPtr); |
334 | |
335 | // If this allocation is responsible for crash, never recycle it. Turn the |
336 | // deallocate() call into a no-op. |
337 | if (Meta->HasCrashed) |
338 | return; |
339 | |
340 | if (Meta->Addr != UPtr) { |
341 | raiseInternallyDetectedError(Address: UPtr, E: Error::INVALID_FREE); |
342 | return; |
343 | } |
344 | if (Meta->IsDeallocated) { |
345 | raiseInternallyDetectedError(Address: UPtr, E: Error::DOUBLE_FREE); |
346 | return; |
347 | } |
348 | |
349 | // Intentionally scope the mutex here, so that other threads can access the |
350 | // pool during the expensive markInaccessible() call. |
351 | { |
352 | ScopedLock L(PoolMutex); |
353 | |
354 | // Ensure that the deallocation is recorded before marking the page as |
355 | // inaccessible. Otherwise, a racy use-after-free will have inconsistent |
356 | // metadata. |
357 | Meta->RecordDeallocation(); |
358 | |
359 | // Ensure that the unwinder is not called if the recursive flag is set, |
360 | // otherwise non-reentrant unwinders may deadlock. |
361 | if (!getThreadLocals()->RecursiveGuard) { |
362 | ScopedRecursiveGuard SRG; |
363 | ScopedLock UL(BacktraceMutex); |
364 | Meta->DeallocationTrace.RecordBacktrace(Backtrace); |
365 | } |
366 | } |
367 | |
368 | deallocateInGuardedPool(Ptr: reinterpret_cast<void *>(SlotStart), |
369 | Size: State.maximumAllocationSize()); |
370 | |
371 | // And finally, lock again to release the slot back into the pool. |
372 | ScopedLock L(PoolMutex); |
373 | freeSlot(SlotIndex: Slot); |
374 | } |
375 | |
376 | // Thread-compatible, protected by PoolMutex. |
377 | static bool PreviousRecursiveGuard; |
378 | |
379 | void GuardedPoolAllocator::preCrashReport(void *Ptr) { |
380 | assert(pointerIsMine(Ptr) && "Pointer is not mine!" ); |
381 | uintptr_t InternalCrashAddr = __gwp_asan_get_internal_crash_address( |
382 | State: &State, ErrorPtr: reinterpret_cast<uintptr_t>(Ptr)); |
383 | if (!InternalCrashAddr) |
384 | disable(); |
385 | |
386 | // If something in the signal handler calls malloc() while dumping the |
387 | // GWP-ASan report (e.g. backtrace_symbols()), make sure that GWP-ASan doesn't |
388 | // service that allocation. `PreviousRecursiveGuard` is protected by the |
389 | // allocator locks taken in disable(), either explicitly above for |
390 | // externally-raised errors, or implicitly in raiseInternallyDetectedError() |
391 | // for internally-detected errors. |
392 | PreviousRecursiveGuard = getThreadLocals()->RecursiveGuard; |
393 | getThreadLocals()->RecursiveGuard = true; |
394 | } |
395 | |
396 | void GuardedPoolAllocator::postCrashReportRecoverableOnly(void *SignalPtr) { |
397 | uintptr_t SignalUPtr = reinterpret_cast<uintptr_t>(SignalPtr); |
398 | uintptr_t InternalCrashAddr = |
399 | __gwp_asan_get_internal_crash_address(State: &State, ErrorPtr: SignalUPtr); |
400 | uintptr_t ErrorUptr = InternalCrashAddr ?: SignalUPtr; |
401 | |
402 | AllocationMetadata *Metadata = addrToMetadata(Ptr: ErrorUptr); |
403 | Metadata->HasCrashed = true; |
404 | |
405 | allocateInGuardedPool( |
406 | Ptr: reinterpret_cast<void *>(getPageAddr(Ptr: SignalUPtr, PageSize: State.PageSize)), |
407 | Size: State.PageSize); |
408 | |
409 | // Clear the internal state in order to not confuse the crash handler if a |
410 | // use-after-free or buffer-overflow comes from a different allocation in the |
411 | // future. |
412 | if (InternalCrashAddr) { |
413 | State.FailureType = Error::UNKNOWN; |
414 | State.FailureAddress = 0; |
415 | } |
416 | |
417 | size_t Slot = State.getNearestSlot(Ptr: ErrorUptr); |
418 | // If the slot is available, remove it permanently. |
419 | for (size_t i = 0; i < FreeSlotsLength; ++i) { |
420 | if (FreeSlots[i] == Slot) { |
421 | FreeSlots[i] = FreeSlots[FreeSlotsLength - 1]; |
422 | FreeSlotsLength -= 1; |
423 | break; |
424 | } |
425 | } |
426 | |
427 | getThreadLocals()->RecursiveGuard = PreviousRecursiveGuard; |
428 | if (!InternalCrashAddr) |
429 | enable(); |
430 | } |
431 | |
432 | size_t GuardedPoolAllocator::getSize(const void *Ptr) { |
433 | assert(pointerIsMine(Ptr)); |
434 | ScopedLock L(PoolMutex); |
435 | AllocationMetadata *Meta = addrToMetadata(Ptr: reinterpret_cast<uintptr_t>(Ptr)); |
436 | assert(Meta->Addr == reinterpret_cast<uintptr_t>(Ptr)); |
437 | return Meta->RequestedSize; |
438 | } |
439 | |
440 | AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr) const { |
441 | return &Metadata[State.getNearestSlot(Ptr)]; |
442 | } |
443 | |
444 | size_t GuardedPoolAllocator::reserveSlot() { |
445 | // Avoid potential reuse of a slot before we have made at least a single |
446 | // allocation in each slot. Helps with our use-after-free detection. |
447 | if (NumSampledAllocations < State.MaxSimultaneousAllocations) |
448 | return NumSampledAllocations++; |
449 | |
450 | if (FreeSlotsLength == 0) |
451 | return kInvalidSlotID; |
452 | |
453 | size_t ReservedIndex = getRandomUnsigned32() % FreeSlotsLength; |
454 | size_t SlotIndex = FreeSlots[ReservedIndex]; |
455 | FreeSlots[ReservedIndex] = FreeSlots[--FreeSlotsLength]; |
456 | return SlotIndex; |
457 | } |
458 | |
459 | void GuardedPoolAllocator::freeSlot(size_t SlotIndex) { |
460 | assert(FreeSlotsLength < State.MaxSimultaneousAllocations); |
461 | FreeSlots[FreeSlotsLength++] = SlotIndex; |
462 | } |
463 | |
464 | uint32_t GuardedPoolAllocator::getRandomUnsigned32() { |
465 | uint32_t RandomState = getThreadLocals()->RandomState; |
466 | RandomState ^= RandomState << 13; |
467 | RandomState ^= RandomState >> 17; |
468 | RandomState ^= RandomState << 5; |
469 | getThreadLocals()->RandomState = RandomState; |
470 | return RandomState; |
471 | } |
472 | } // namespace gwp_asan |
473 | |