11f9cb04fSpatrick //===-- common.cpp ----------------------------------------------*- C++ -*-===// 21f9cb04fSpatrick // 31f9cb04fSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41f9cb04fSpatrick // See https://llvm.org/LICENSE.txt for license information. 51f9cb04fSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61f9cb04fSpatrick // 71f9cb04fSpatrick //===----------------------------------------------------------------------===// 81f9cb04fSpatrick 91f9cb04fSpatrick #include "gwp_asan/common.h" 101f9cb04fSpatrick #include "gwp_asan/stack_trace_compressor.h" 111f9cb04fSpatrick 121f9cb04fSpatrick #include <assert.h> 131f9cb04fSpatrick 141f9cb04fSpatrick using AllocationMetadata = gwp_asan::AllocationMetadata; 151f9cb04fSpatrick using Error = gwp_asan::Error; 161f9cb04fSpatrick 171f9cb04fSpatrick namespace gwp_asan { 181f9cb04fSpatrick 191f9cb04fSpatrick const char *ErrorToString(const Error &E) { 201f9cb04fSpatrick switch (E) { 211f9cb04fSpatrick case Error::UNKNOWN: 221f9cb04fSpatrick return "Unknown"; 231f9cb04fSpatrick case Error::USE_AFTER_FREE: 241f9cb04fSpatrick return "Use After Free"; 251f9cb04fSpatrick case Error::DOUBLE_FREE: 261f9cb04fSpatrick return "Double Free"; 271f9cb04fSpatrick case Error::INVALID_FREE: 281f9cb04fSpatrick return "Invalid (Wild) Free"; 291f9cb04fSpatrick case Error::BUFFER_OVERFLOW: 301f9cb04fSpatrick return "Buffer Overflow"; 311f9cb04fSpatrick case Error::BUFFER_UNDERFLOW: 321f9cb04fSpatrick return "Buffer Underflow"; 331f9cb04fSpatrick } 341f9cb04fSpatrick __builtin_trap(); 351f9cb04fSpatrick } 361f9cb04fSpatrick 37*d89ec533Spatrick constexpr size_t AllocationMetadata::kStackFrameStorageBytes; 38*d89ec533Spatrick constexpr size_t AllocationMetadata::kMaxTraceLengthToCollect; 39*d89ec533Spatrick 401f9cb04fSpatrick void AllocationMetadata::RecordAllocation(uintptr_t AllocAddr, 411f9cb04fSpatrick size_t AllocSize) { 421f9cb04fSpatrick Addr = AllocAddr; 43*d89ec533Spatrick RequestedSize = AllocSize; 441f9cb04fSpatrick IsDeallocated = false; 451f9cb04fSpatrick 461f9cb04fSpatrick AllocationTrace.ThreadID = getThreadID(); 471f9cb04fSpatrick DeallocationTrace.TraceSize = 0; 481f9cb04fSpatrick DeallocationTrace.ThreadID = kInvalidThreadID; 491f9cb04fSpatrick } 501f9cb04fSpatrick 511f9cb04fSpatrick void AllocationMetadata::RecordDeallocation() { 521f9cb04fSpatrick IsDeallocated = true; 531f9cb04fSpatrick DeallocationTrace.ThreadID = getThreadID(); 541f9cb04fSpatrick } 551f9cb04fSpatrick 561f9cb04fSpatrick void AllocationMetadata::CallSiteInfo::RecordBacktrace( 571f9cb04fSpatrick options::Backtrace_t Backtrace) { 581f9cb04fSpatrick TraceSize = 0; 591f9cb04fSpatrick if (!Backtrace) 601f9cb04fSpatrick return; 611f9cb04fSpatrick 621f9cb04fSpatrick uintptr_t UncompressedBuffer[kMaxTraceLengthToCollect]; 631f9cb04fSpatrick size_t BacktraceLength = 641f9cb04fSpatrick Backtrace(UncompressedBuffer, kMaxTraceLengthToCollect); 651f9cb04fSpatrick // Backtrace() returns the number of available frames, which may be greater 661f9cb04fSpatrick // than the number of frames in the buffer. In this case, we need to only pack 671f9cb04fSpatrick // the number of frames that are in the buffer. 681f9cb04fSpatrick if (BacktraceLength > kMaxTraceLengthToCollect) 691f9cb04fSpatrick BacktraceLength = kMaxTraceLengthToCollect; 701f9cb04fSpatrick TraceSize = 711f9cb04fSpatrick compression::pack(UncompressedBuffer, BacktraceLength, CompressedTrace, 721f9cb04fSpatrick AllocationMetadata::kStackFrameStorageBytes); 731f9cb04fSpatrick } 741f9cb04fSpatrick 751f9cb04fSpatrick size_t AllocatorState::maximumAllocationSize() const { return PageSize; } 761f9cb04fSpatrick 771f9cb04fSpatrick uintptr_t AllocatorState::slotToAddr(size_t N) const { 781f9cb04fSpatrick return GuardedPagePool + (PageSize * (1 + N)) + (maximumAllocationSize() * N); 791f9cb04fSpatrick } 801f9cb04fSpatrick 811f9cb04fSpatrick bool AllocatorState::isGuardPage(uintptr_t Ptr) const { 821f9cb04fSpatrick assert(pointerIsMine(reinterpret_cast<void *>(Ptr))); 831f9cb04fSpatrick size_t PageOffsetFromPoolStart = (Ptr - GuardedPagePool) / PageSize; 841f9cb04fSpatrick size_t PagesPerSlot = maximumAllocationSize() / PageSize; 851f9cb04fSpatrick return (PageOffsetFromPoolStart % (PagesPerSlot + 1)) == 0; 861f9cb04fSpatrick } 871f9cb04fSpatrick 881f9cb04fSpatrick static size_t addrToSlot(const AllocatorState *State, uintptr_t Ptr) { 891f9cb04fSpatrick size_t ByteOffsetFromPoolStart = Ptr - State->GuardedPagePool; 901f9cb04fSpatrick return ByteOffsetFromPoolStart / 911f9cb04fSpatrick (State->maximumAllocationSize() + State->PageSize); 921f9cb04fSpatrick } 931f9cb04fSpatrick 941f9cb04fSpatrick size_t AllocatorState::getNearestSlot(uintptr_t Ptr) const { 951f9cb04fSpatrick if (Ptr <= GuardedPagePool + PageSize) 961f9cb04fSpatrick return 0; 971f9cb04fSpatrick if (Ptr > GuardedPagePoolEnd - PageSize) 981f9cb04fSpatrick return MaxSimultaneousAllocations - 1; 991f9cb04fSpatrick 1001f9cb04fSpatrick if (!isGuardPage(Ptr)) 1011f9cb04fSpatrick return addrToSlot(this, Ptr); 1021f9cb04fSpatrick 1031f9cb04fSpatrick if (Ptr % PageSize <= PageSize / 2) 1041f9cb04fSpatrick return addrToSlot(this, Ptr - PageSize); // Round down. 1051f9cb04fSpatrick return addrToSlot(this, Ptr + PageSize); // Round up. 1061f9cb04fSpatrick } 1071f9cb04fSpatrick 1081f9cb04fSpatrick } // namespace gwp_asan 109