11f9cb04fSpatrick //===-- common.h ------------------------------------------------*- 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 // This file contains code that is common between the crash handler and the 101f9cb04fSpatrick // GuardedPoolAllocator. 111f9cb04fSpatrick 121f9cb04fSpatrick #ifndef GWP_ASAN_COMMON_H_ 131f9cb04fSpatrick #define GWP_ASAN_COMMON_H_ 141f9cb04fSpatrick 151f9cb04fSpatrick #include "gwp_asan/definitions.h" 161f9cb04fSpatrick #include "gwp_asan/options.h" 171f9cb04fSpatrick 181f9cb04fSpatrick #include <stddef.h> 191f9cb04fSpatrick #include <stdint.h> 201f9cb04fSpatrick 211f9cb04fSpatrick namespace gwp_asan { 22*810390e3Srobert 23*810390e3Srobert // Magic header that resides in the AllocatorState so that GWP-ASan bugreports 24*810390e3Srobert // can be understood by tools at different versions. Out-of-process crash 25*810390e3Srobert // handlers, like crashpad on Fuchsia, take the raw contents of the 26*810390e3Srobert // AllocationMetatada array and the AllocatorState, and shove them into the 27*810390e3Srobert // minidump. Online unpacking of these structs needs to know from which version 28*810390e3Srobert // of GWP-ASan it's extracting the information, as the structures are not 29*810390e3Srobert // stable. 30*810390e3Srobert struct AllocatorVersionMagic { 31*810390e3Srobert // The values are copied into the structure at runtime, during 32*810390e3Srobert // `GuardedPoolAllocator::init()` so that GWP-ASan remains completely in the 33*810390e3Srobert // `.bss` segment. 34*810390e3Srobert static constexpr uint8_t kAllocatorVersionMagic[4] = {'A', 'S', 'A', 'N'}; 35*810390e3Srobert uint8_t Magic[4] = {}; 36*810390e3Srobert // Update the version number when the AllocatorState or AllocationMetadata 37*810390e3Srobert // change. 38*810390e3Srobert static constexpr uint16_t kAllocatorVersion = 2; 39*810390e3Srobert uint16_t Version = 0; 40*810390e3Srobert uint16_t Reserved = 0; 41*810390e3Srobert }; 42*810390e3Srobert 43*810390e3Srobert enum class Error : uint8_t { 441f9cb04fSpatrick UNKNOWN, 451f9cb04fSpatrick USE_AFTER_FREE, 461f9cb04fSpatrick DOUBLE_FREE, 471f9cb04fSpatrick INVALID_FREE, 481f9cb04fSpatrick BUFFER_OVERFLOW, 491f9cb04fSpatrick BUFFER_UNDERFLOW 501f9cb04fSpatrick }; 511f9cb04fSpatrick 521f9cb04fSpatrick const char *ErrorToString(const Error &E); 531f9cb04fSpatrick 541f9cb04fSpatrick static constexpr uint64_t kInvalidThreadID = UINT64_MAX; 551f9cb04fSpatrick // Get the current thread ID, or kInvalidThreadID if failure. Note: This 561f9cb04fSpatrick // implementation is platform-specific. 571f9cb04fSpatrick uint64_t getThreadID(); 581f9cb04fSpatrick 591f9cb04fSpatrick // This struct contains all the metadata recorded about a single allocation made 601f9cb04fSpatrick // by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid. 611f9cb04fSpatrick struct AllocationMetadata { 621f9cb04fSpatrick // The number of bytes used to store a compressed stack frame. On 64-bit 631f9cb04fSpatrick // platforms, assuming a compression ratio of 50%, this should allow us to 641f9cb04fSpatrick // store ~64 frames per trace. 651f9cb04fSpatrick static constexpr size_t kStackFrameStorageBytes = 256; 661f9cb04fSpatrick 671f9cb04fSpatrick // Maximum number of stack frames to collect on allocation/deallocation. The 681f9cb04fSpatrick // actual number of collected frames may be less than this as the stack 691f9cb04fSpatrick // frames are compressed into a fixed memory range. 701f9cb04fSpatrick static constexpr size_t kMaxTraceLengthToCollect = 128; 711f9cb04fSpatrick 721f9cb04fSpatrick // Records the given allocation metadata into this struct. 73d89ec533Spatrick void RecordAllocation(uintptr_t Addr, size_t RequestedSize); 741f9cb04fSpatrick // Record that this allocation is now deallocated. 751f9cb04fSpatrick void RecordDeallocation(); 761f9cb04fSpatrick 771f9cb04fSpatrick struct CallSiteInfo { 781f9cb04fSpatrick // Record the current backtrace to this callsite. 791f9cb04fSpatrick void RecordBacktrace(options::Backtrace_t Backtrace); 801f9cb04fSpatrick 811f9cb04fSpatrick // The compressed backtrace to the allocation/deallocation. 821f9cb04fSpatrick uint8_t CompressedTrace[kStackFrameStorageBytes]; 831f9cb04fSpatrick // The thread ID for this trace, or kInvalidThreadID if not available. 841f9cb04fSpatrick uint64_t ThreadID = kInvalidThreadID; 851f9cb04fSpatrick // The size of the compressed trace (in bytes). Zero indicates that no 861f9cb04fSpatrick // trace was collected. 871f9cb04fSpatrick size_t TraceSize = 0; 881f9cb04fSpatrick }; 891f9cb04fSpatrick 901f9cb04fSpatrick // The address of this allocation. If zero, the rest of this struct isn't 911f9cb04fSpatrick // valid, as the allocation has never occurred. 921f9cb04fSpatrick uintptr_t Addr = 0; 931f9cb04fSpatrick // Represents the actual size of the allocation. 94d89ec533Spatrick size_t RequestedSize = 0; 951f9cb04fSpatrick 961f9cb04fSpatrick CallSiteInfo AllocationTrace; 971f9cb04fSpatrick CallSiteInfo DeallocationTrace; 981f9cb04fSpatrick 991f9cb04fSpatrick // Whether this allocation has been deallocated yet. 1001f9cb04fSpatrick bool IsDeallocated = false; 101*810390e3Srobert 102*810390e3Srobert // In recoverable mode, whether this allocation has had a crash associated 103*810390e3Srobert // with it. This has certain side effects, like meaning this allocation will 104*810390e3Srobert // permanently occupy a slot, and won't ever have another crash reported from 105*810390e3Srobert // it. 106*810390e3Srobert bool HasCrashed = false; 1071f9cb04fSpatrick }; 1081f9cb04fSpatrick 1091f9cb04fSpatrick // This holds the state that's shared between the GWP-ASan allocator and the 1101f9cb04fSpatrick // crash handler. This, in conjunction with the Metadata array, forms the entire 1111f9cb04fSpatrick // set of information required for understanding a GWP-ASan crash. 1121f9cb04fSpatrick struct AllocatorState { AllocatorStateAllocatorState113d89ec533Spatrick constexpr AllocatorState() {} 114*810390e3Srobert AllocatorVersionMagic VersionMagic{}; 115d89ec533Spatrick 1161f9cb04fSpatrick // Returns whether the provided pointer is a current sampled allocation that 1171f9cb04fSpatrick // is owned by this pool. pointerIsMineAllocatorState1181f9cb04fSpatrick GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const { 1191f9cb04fSpatrick uintptr_t P = reinterpret_cast<uintptr_t>(Ptr); 1201f9cb04fSpatrick return P < GuardedPagePoolEnd && GuardedPagePool <= P; 1211f9cb04fSpatrick } 1221f9cb04fSpatrick 1231f9cb04fSpatrick // Returns the address of the N-th guarded slot. 1241f9cb04fSpatrick uintptr_t slotToAddr(size_t N) const; 1251f9cb04fSpatrick 1261f9cb04fSpatrick // Returns the largest allocation that is supported by this pool. 1271f9cb04fSpatrick size_t maximumAllocationSize() const; 1281f9cb04fSpatrick 1291f9cb04fSpatrick // Gets the nearest slot to the provided address. 1301f9cb04fSpatrick size_t getNearestSlot(uintptr_t Ptr) const; 1311f9cb04fSpatrick 1321f9cb04fSpatrick // Returns whether the provided pointer is a guard page or not. The pointer 1331f9cb04fSpatrick // must be within memory owned by this pool, else the result is undefined. 1341f9cb04fSpatrick bool isGuardPage(uintptr_t Ptr) const; 1351f9cb04fSpatrick 136*810390e3Srobert // Returns the address that's used by __gwp_asan_get_internal_crash_address() 137*810390e3Srobert // and GPA::raiseInternallyDetectedError() to communicate that the SEGV in 138*810390e3Srobert // question comes from an internally-detected error. 139*810390e3Srobert uintptr_t internallyDetectedErrorFaultAddress() const; 140*810390e3Srobert 1411f9cb04fSpatrick // The number of guarded slots that this pool holds. 1421f9cb04fSpatrick size_t MaxSimultaneousAllocations = 0; 1431f9cb04fSpatrick 1441f9cb04fSpatrick // Pointer to the pool of guarded slots. Note that this points to the start of 1451f9cb04fSpatrick // the pool (which is a guard page), not a pointer to the first guarded page. 1461f9cb04fSpatrick uintptr_t GuardedPagePool = 0; 1471f9cb04fSpatrick uintptr_t GuardedPagePoolEnd = 0; 1481f9cb04fSpatrick 1491f9cb04fSpatrick // Cached page size for this system in bytes. 1501f9cb04fSpatrick size_t PageSize = 0; 1511f9cb04fSpatrick 1521f9cb04fSpatrick // The type and address of an internally-detected failure. For INVALID_FREE 1531f9cb04fSpatrick // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set 1541f9cb04fSpatrick // these values and terminate the process. 1551f9cb04fSpatrick Error FailureType = Error::UNKNOWN; 1561f9cb04fSpatrick uintptr_t FailureAddress = 0; 1571f9cb04fSpatrick }; 1581f9cb04fSpatrick 159*810390e3Srobert // Below are various compile-time checks that the layout of the internal 160*810390e3Srobert // GWP-ASan structures are undisturbed. If they are disturbed, the version magic 161*810390e3Srobert // number needs to be increased by one, and the asserts need to be updated. 162*810390e3Srobert // Out-of-process crash handlers, like breakpad/crashpad, may copy the internal 163*810390e3Srobert // GWP-ASan structures into a minidump for offline reconstruction of the crash. 164*810390e3Srobert // In order to accomplish this, the offline reconstructor needs to know the 165*810390e3Srobert // version of GWP-ASan internal structures that it's unpacking (along with the 166*810390e3Srobert // architecture-specific layout info, which is left as an exercise to the crash 167*810390e3Srobert // handler). 168*810390e3Srobert static_assert(offsetof(AllocatorState, VersionMagic) == 0, ""); 169*810390e3Srobert static_assert(sizeof(AllocatorVersionMagic) == 8, ""); 170*810390e3Srobert #if defined(__x86_64__) 171*810390e3Srobert static_assert(sizeof(AllocatorState) == 56, ""); 172*810390e3Srobert static_assert(offsetof(AllocatorState, FailureAddress) == 48, ""); 173*810390e3Srobert static_assert(sizeof(AllocationMetadata) == 568, ""); 174*810390e3Srobert static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, ""); 175*810390e3Srobert #elif defined(__aarch64__) 176*810390e3Srobert static_assert(sizeof(AllocatorState) == 56, ""); 177*810390e3Srobert static_assert(offsetof(AllocatorState, FailureAddress) == 48, ""); 178*810390e3Srobert static_assert(sizeof(AllocationMetadata) == 568, ""); 179*810390e3Srobert static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, ""); 180*810390e3Srobert #elif defined(__i386__) 181*810390e3Srobert static_assert(sizeof(AllocatorState) == 32, ""); 182*810390e3Srobert static_assert(offsetof(AllocatorState, FailureAddress) == 28, ""); 183*810390e3Srobert static_assert(sizeof(AllocationMetadata) == 548, ""); 184*810390e3Srobert static_assert(offsetof(AllocationMetadata, IsDeallocated) == 544, ""); 185*810390e3Srobert #elif defined(__arm__) 186*810390e3Srobert static_assert(sizeof(AllocatorState) == 32, ""); 187*810390e3Srobert static_assert(offsetof(AllocatorState, FailureAddress) == 28, ""); 188*810390e3Srobert static_assert(sizeof(AllocationMetadata) == 560, ""); 189*810390e3Srobert static_assert(offsetof(AllocationMetadata, IsDeallocated) == 552, ""); 190*810390e3Srobert #endif // defined($ARCHITECTURE) 191*810390e3Srobert 1921f9cb04fSpatrick } // namespace gwp_asan 1931f9cb04fSpatrick #endif // GWP_ASAN_COMMON_H_ 194