| 1 | //===-- common.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 | // This file contains code that is common between the crash handler and the |
| 10 | // GuardedPoolAllocator. |
| 11 | |
| 12 | #ifndef GWP_ASAN_COMMON_H_ |
| 13 | #define GWP_ASAN_COMMON_H_ |
| 14 | |
| 15 | #include "gwp_asan/definitions.h" |
| 16 | #include "gwp_asan/options.h" |
| 17 | |
| 18 | #include <stddef.h> |
| 19 | #include <stdint.h> |
| 20 | |
| 21 | namespace gwp_asan { |
| 22 | |
| 23 | // Magic header that resides in the AllocatorState so that GWP-ASan bugreports |
| 24 | // can be understood by tools at different versions. Out-of-process crash |
| 25 | // handlers, like crashpad on Fuchsia, take the raw contents of the |
| 26 | // AllocationMetatada array and the AllocatorState, and shove them into the |
| 27 | // minidump. Online unpacking of these structs needs to know from which version |
| 28 | // of GWP-ASan it's extracting the information, as the structures are not |
| 29 | // stable. |
| 30 | struct AllocatorVersionMagic { |
| 31 | // The values are copied into the structure at runtime, during |
| 32 | // `GuardedPoolAllocator::init()` so that GWP-ASan remains completely in the |
| 33 | // `.bss` segment. |
| 34 | static constexpr uint8_t kAllocatorVersionMagic[4] = {'A', 'S', 'A', 'N'}; |
| 35 | uint8_t Magic[4] = {}; |
| 36 | // Update the version number when the AllocatorState or AllocationMetadata |
| 37 | // change. |
| 38 | static constexpr uint16_t kAllocatorVersion = 2; |
| 39 | uint16_t Version = 0; |
| 40 | uint16_t Reserved = 0; |
| 41 | }; |
| 42 | |
| 43 | enum class Error : uint8_t { |
| 44 | UNKNOWN, |
| 45 | USE_AFTER_FREE, |
| 46 | DOUBLE_FREE, |
| 47 | INVALID_FREE, |
| 48 | BUFFER_OVERFLOW, |
| 49 | BUFFER_UNDERFLOW |
| 50 | }; |
| 51 | |
| 52 | const char *ErrorToString(const Error &E); |
| 53 | |
| 54 | static constexpr uint64_t kInvalidThreadID = UINT64_MAX; |
| 55 | // Get the current thread ID, or kInvalidThreadID if failure. Note: This |
| 56 | // implementation is platform-specific. |
| 57 | uint64_t getThreadID(); |
| 58 | |
| 59 | // This struct contains all the metadata recorded about a single allocation made |
| 60 | // by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid. |
| 61 | struct AllocationMetadata { |
| 62 | // The number of bytes used to store a compressed stack frame. On 64-bit |
| 63 | // platforms, assuming a compression ratio of 50%, this should allow us to |
| 64 | // store ~64 frames per trace. |
| 65 | static constexpr size_t kStackFrameStorageBytes = 256; |
| 66 | |
| 67 | // Maximum number of stack frames to collect on allocation/deallocation. The |
| 68 | // actual number of collected frames may be less than this as the stack |
| 69 | // frames are compressed into a fixed memory range. |
| 70 | static constexpr size_t kMaxTraceLengthToCollect = 128; |
| 71 | |
| 72 | // Records the given allocation metadata into this struct. |
| 73 | void RecordAllocation(uintptr_t Addr, size_t RequestedSize); |
| 74 | // Record that this allocation is now deallocated. |
| 75 | void RecordDeallocation(); |
| 76 | |
| 77 | struct CallSiteInfo { |
| 78 | // Record the current backtrace to this callsite. |
| 79 | void RecordBacktrace(options::Backtrace_t Backtrace); |
| 80 | |
| 81 | // The compressed backtrace to the allocation/deallocation. |
| 82 | uint8_t CompressedTrace[kStackFrameStorageBytes]; |
| 83 | // The thread ID for this trace, or kInvalidThreadID if not available. |
| 84 | uint64_t ThreadID = kInvalidThreadID; |
| 85 | // The size of the compressed trace (in bytes). Zero indicates that no |
| 86 | // trace was collected. |
| 87 | size_t TraceSize = 0; |
| 88 | }; |
| 89 | |
| 90 | // The address of this allocation. If zero, the rest of this struct isn't |
| 91 | // valid, as the allocation has never occurred. |
| 92 | uintptr_t Addr = 0; |
| 93 | // Represents the actual size of the allocation. |
| 94 | size_t RequestedSize = 0; |
| 95 | |
| 96 | CallSiteInfo AllocationTrace; |
| 97 | CallSiteInfo DeallocationTrace; |
| 98 | |
| 99 | // Whether this allocation has been deallocated yet. |
| 100 | bool IsDeallocated = false; |
| 101 | |
| 102 | // In recoverable mode, whether this allocation has had a crash associated |
| 103 | // with it. This has certain side effects, like meaning this allocation will |
| 104 | // permanently occupy a slot, and won't ever have another crash reported from |
| 105 | // it. |
| 106 | bool HasCrashed = false; |
| 107 | }; |
| 108 | |
| 109 | // This holds the state that's shared between the GWP-ASan allocator and the |
| 110 | // crash handler. This, in conjunction with the Metadata array, forms the entire |
| 111 | // set of information required for understanding a GWP-ASan crash. |
| 112 | struct AllocatorState { |
| 113 | constexpr AllocatorState() {} |
| 114 | AllocatorVersionMagic VersionMagic{}; |
| 115 | |
| 116 | // Returns whether the provided pointer is a current sampled allocation that |
| 117 | // is owned by this pool. |
| 118 | GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const { |
| 119 | uintptr_t P = reinterpret_cast<uintptr_t>(Ptr); |
| 120 | return P < GuardedPagePoolEnd && GuardedPagePool <= P; |
| 121 | } |
| 122 | |
| 123 | // Returns the address of the N-th guarded slot. |
| 124 | uintptr_t slotToAddr(size_t N) const; |
| 125 | |
| 126 | // Returns the largest allocation that is supported by this pool. |
| 127 | size_t maximumAllocationSize() const; |
| 128 | |
| 129 | // Gets the nearest slot to the provided address. |
| 130 | size_t getNearestSlot(uintptr_t Ptr) const; |
| 131 | |
| 132 | // Returns whether the provided pointer is a guard page or not. The pointer |
| 133 | // must be within memory owned by this pool, else the result is undefined. |
| 134 | bool isGuardPage(uintptr_t Ptr) const; |
| 135 | |
| 136 | // Returns the address that's used by __gwp_asan_get_internal_crash_address() |
| 137 | // and GPA::raiseInternallyDetectedError() to communicate that the SEGV in |
| 138 | // question comes from an internally-detected error. |
| 139 | uintptr_t internallyDetectedErrorFaultAddress() const; |
| 140 | |
| 141 | // The number of guarded slots that this pool holds. |
| 142 | size_t MaxSimultaneousAllocations = 0; |
| 143 | |
| 144 | // Pointer to the pool of guarded slots. Note that this points to the start of |
| 145 | // the pool (which is a guard page), not a pointer to the first guarded page. |
| 146 | uintptr_t GuardedPagePool = 0; |
| 147 | uintptr_t GuardedPagePoolEnd = 0; |
| 148 | |
| 149 | // Cached page size for this system in bytes. |
| 150 | size_t PageSize = 0; |
| 151 | |
| 152 | // The type and address of an internally-detected failure. For INVALID_FREE |
| 153 | // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set |
| 154 | // these values and terminate the process. |
| 155 | Error FailureType = Error::UNKNOWN; |
| 156 | uintptr_t FailureAddress = 0; |
| 157 | }; |
| 158 | |
| 159 | // Below are various compile-time checks that the layout of the internal |
| 160 | // GWP-ASan structures are undisturbed. If they are disturbed, the version magic |
| 161 | // number needs to be increased by one, and the asserts need to be updated. |
| 162 | // Out-of-process crash handlers, like breakpad/crashpad, may copy the internal |
| 163 | // GWP-ASan structures into a minidump for offline reconstruction of the crash. |
| 164 | // In order to accomplish this, the offline reconstructor needs to know the |
| 165 | // version of GWP-ASan internal structures that it's unpacking (along with the |
| 166 | // architecture-specific layout info, which is left as an exercise to the crash |
| 167 | // handler). |
| 168 | static_assert(offsetof(AllocatorState, VersionMagic) == 0, "" ); |
| 169 | static_assert(sizeof(AllocatorVersionMagic) == 8, "" ); |
| 170 | #if defined(__x86_64__) |
| 171 | static_assert(sizeof(AllocatorState) == 56, "" ); |
| 172 | static_assert(offsetof(AllocatorState, FailureAddress) == 48, "" ); |
| 173 | static_assert(sizeof(AllocationMetadata) == 568, "" ); |
| 174 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "" ); |
| 175 | #elif defined(__aarch64__) |
| 176 | static_assert(sizeof(AllocatorState) == 56, "" ); |
| 177 | static_assert(offsetof(AllocatorState, FailureAddress) == 48, "" ); |
| 178 | static_assert(sizeof(AllocationMetadata) == 568, "" ); |
| 179 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "" ); |
| 180 | #elif defined(__i386__) |
| 181 | static_assert(sizeof(AllocatorState) == 32, "" ); |
| 182 | static_assert(offsetof(AllocatorState, FailureAddress) == 28, "" ); |
| 183 | static_assert(sizeof(AllocationMetadata) == 548, "" ); |
| 184 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 544, "" ); |
| 185 | #elif defined(__arm__) |
| 186 | static_assert(sizeof(AllocatorState) == 32, "" ); |
| 187 | static_assert(offsetof(AllocatorState, FailureAddress) == 28, "" ); |
| 188 | static_assert(sizeof(AllocationMetadata) == 560, "" ); |
| 189 | static_assert(offsetof(AllocationMetadata, IsDeallocated) == 552, "" ); |
| 190 | #endif // defined($ARCHITECTURE) |
| 191 | |
| 192 | } // namespace gwp_asan |
| 193 | #endif // GWP_ASAN_COMMON_H_ |
| 194 | |