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