1 //===-- common.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/common.h" 10 #include "gwp_asan/stack_trace_compressor.h" 11 12 #include <assert.h> 13 14 using AllocationMetadata = gwp_asan::AllocationMetadata; 15 using Error = gwp_asan::Error; 16 17 namespace gwp_asan { 18 19 const char *ErrorToString(const Error &E) { 20 switch (E) { 21 case Error::UNKNOWN: 22 return "Unknown"; 23 case Error::USE_AFTER_FREE: 24 return "Use After Free"; 25 case Error::DOUBLE_FREE: 26 return "Double Free"; 27 case Error::INVALID_FREE: 28 return "Invalid (Wild) Free"; 29 case Error::BUFFER_OVERFLOW: 30 return "Buffer Overflow"; 31 case Error::BUFFER_UNDERFLOW: 32 return "Buffer Underflow"; 33 } 34 __builtin_trap(); 35 } 36 37 constexpr size_t AllocationMetadata::kStackFrameStorageBytes; 38 constexpr size_t AllocationMetadata::kMaxTraceLengthToCollect; 39 40 void AllocationMetadata::RecordAllocation(uintptr_t AllocAddr, 41 size_t AllocSize) { 42 Addr = AllocAddr; 43 RequestedSize = AllocSize; 44 IsDeallocated = false; 45 46 AllocationTrace.ThreadID = getThreadID(); 47 DeallocationTrace.TraceSize = 0; 48 DeallocationTrace.ThreadID = kInvalidThreadID; 49 } 50 51 void AllocationMetadata::RecordDeallocation() { 52 IsDeallocated = true; 53 DeallocationTrace.ThreadID = getThreadID(); 54 } 55 56 void AllocationMetadata::CallSiteInfo::RecordBacktrace( 57 options::Backtrace_t Backtrace) { 58 TraceSize = 0; 59 if (!Backtrace) 60 return; 61 62 uintptr_t UncompressedBuffer[kMaxTraceLengthToCollect]; 63 size_t BacktraceLength = 64 Backtrace(UncompressedBuffer, kMaxTraceLengthToCollect); 65 // Backtrace() returns the number of available frames, which may be greater 66 // than the number of frames in the buffer. In this case, we need to only pack 67 // the number of frames that are in the buffer. 68 if (BacktraceLength > kMaxTraceLengthToCollect) 69 BacktraceLength = kMaxTraceLengthToCollect; 70 TraceSize = 71 compression::pack(UncompressedBuffer, BacktraceLength, CompressedTrace, 72 AllocationMetadata::kStackFrameStorageBytes); 73 } 74 75 size_t AllocatorState::maximumAllocationSize() const { return PageSize; } 76 77 uintptr_t AllocatorState::slotToAddr(size_t N) const { 78 return GuardedPagePool + (PageSize * (1 + N)) + (maximumAllocationSize() * N); 79 } 80 81 bool AllocatorState::isGuardPage(uintptr_t Ptr) const { 82 assert(pointerIsMine(reinterpret_cast<void *>(Ptr))); 83 size_t PageOffsetFromPoolStart = (Ptr - GuardedPagePool) / PageSize; 84 size_t PagesPerSlot = maximumAllocationSize() / PageSize; 85 return (PageOffsetFromPoolStart % (PagesPerSlot + 1)) == 0; 86 } 87 88 static size_t addrToSlot(const AllocatorState *State, uintptr_t Ptr) { 89 size_t ByteOffsetFromPoolStart = Ptr - State->GuardedPagePool; 90 return ByteOffsetFromPoolStart / 91 (State->maximumAllocationSize() + State->PageSize); 92 } 93 94 size_t AllocatorState::getNearestSlot(uintptr_t Ptr) const { 95 if (Ptr <= GuardedPagePool + PageSize) 96 return 0; 97 if (Ptr > GuardedPagePoolEnd - PageSize) 98 return MaxSimultaneousAllocations - 1; 99 100 if (!isGuardPage(Ptr)) 101 return addrToSlot(this, Ptr); 102 103 if (Ptr % PageSize <= PageSize / 2) 104 return addrToSlot(this, Ptr - PageSize); // Round down. 105 return addrToSlot(this, Ptr + PageSize); // Round up. 106 } 107 108 } // namespace gwp_asan 109