xref: /llvm-project/compiler-rt/lib/gwp_asan/common.h (revision 35b5499d7259ac3e5c648a711678290695703a87)
1a6258684SMitch Phillips //===-- common.h ------------------------------------------------*- C++ -*-===//
2a6258684SMitch Phillips //
3a6258684SMitch Phillips // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a6258684SMitch Phillips // See https://llvm.org/LICENSE.txt for license information.
5a6258684SMitch Phillips // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a6258684SMitch Phillips //
7a6258684SMitch Phillips //===----------------------------------------------------------------------===//
8a6258684SMitch Phillips 
9a6258684SMitch Phillips // This file contains code that is common between the crash handler and the
10a6258684SMitch Phillips // GuardedPoolAllocator.
11a6258684SMitch Phillips 
12a6258684SMitch Phillips #ifndef GWP_ASAN_COMMON_H_
13a6258684SMitch Phillips #define GWP_ASAN_COMMON_H_
14a6258684SMitch Phillips 
15a6258684SMitch Phillips #include "gwp_asan/definitions.h"
16a6258684SMitch Phillips #include "gwp_asan/options.h"
17a6258684SMitch Phillips 
18a6258684SMitch Phillips #include <stddef.h>
19a6258684SMitch Phillips #include <stdint.h>
20a6258684SMitch Phillips 
21a6258684SMitch Phillips namespace gwp_asan {
228e167f66SMitch Phillips 
238e167f66SMitch Phillips // Magic header that resides in the AllocatorState so that GWP-ASan bugreports
248e167f66SMitch Phillips // can be understood by tools at different versions. Out-of-process crash
2504f59133SKostya Kortchinsky // handlers, like crashpad on Fuchsia, take the raw contents of the
268e167f66SMitch Phillips // AllocationMetatada array and the AllocatorState, and shove them into the
278e167f66SMitch Phillips // minidump. Online unpacking of these structs needs to know from which version
2804f59133SKostya Kortchinsky // of GWP-ASan it's extracting the information, as the structures are not
2904f59133SKostya Kortchinsky // stable.
308e167f66SMitch Phillips struct AllocatorVersionMagic {
3104f59133SKostya Kortchinsky   // The values are copied into the structure at runtime, during
3204f59133SKostya Kortchinsky   // `GuardedPoolAllocator::init()` so that GWP-ASan remains completely in the
3304f59133SKostya Kortchinsky   // `.bss` segment.
3404f59133SKostya Kortchinsky   static constexpr uint8_t kAllocatorVersionMagic[4] = {'A', 'S', 'A', 'N'};
3504f59133SKostya Kortchinsky   uint8_t Magic[4] = {};
368e167f66SMitch Phillips   // Update the version number when the AllocatorState or AllocationMetadata
378e167f66SMitch Phillips   // change.
38*35b5499dSMitch Phillips   static constexpr uint16_t kAllocatorVersion = 2;
3904f59133SKostya Kortchinsky   uint16_t Version = 0;
4004f59133SKostya Kortchinsky   uint16_t Reserved = 0;
418e167f66SMitch Phillips };
428e167f66SMitch Phillips 
438e167f66SMitch Phillips enum class Error : uint8_t {
44a6258684SMitch Phillips   UNKNOWN,
45a6258684SMitch Phillips   USE_AFTER_FREE,
46a6258684SMitch Phillips   DOUBLE_FREE,
47a6258684SMitch Phillips   INVALID_FREE,
48a6258684SMitch Phillips   BUFFER_OVERFLOW,
49a6258684SMitch Phillips   BUFFER_UNDERFLOW
50a6258684SMitch Phillips };
51a6258684SMitch Phillips 
52a6258684SMitch Phillips const char *ErrorToString(const Error &E);
53a6258684SMitch Phillips 
54a6258684SMitch Phillips static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
55a6258684SMitch Phillips // Get the current thread ID, or kInvalidThreadID if failure. Note: This
56a6258684SMitch Phillips // implementation is platform-specific.
57a6258684SMitch Phillips uint64_t getThreadID();
58a6258684SMitch Phillips 
59a6258684SMitch Phillips // This struct contains all the metadata recorded about a single allocation made
60a6258684SMitch Phillips // by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid.
61a6258684SMitch Phillips struct AllocationMetadata {
62a6258684SMitch Phillips   // The number of bytes used to store a compressed stack frame. On 64-bit
63a6258684SMitch Phillips   // platforms, assuming a compression ratio of 50%, this should allow us to
64a6258684SMitch Phillips   // store ~64 frames per trace.
65a6258684SMitch Phillips   static constexpr size_t kStackFrameStorageBytes = 256;
66a6258684SMitch Phillips 
67a6258684SMitch Phillips   // Maximum number of stack frames to collect on allocation/deallocation. The
68a6258684SMitch Phillips   // actual number of collected frames may be less than this as the stack
69a6258684SMitch Phillips   // frames are compressed into a fixed memory range.
70a6258684SMitch Phillips   static constexpr size_t kMaxTraceLengthToCollect = 128;
71a6258684SMitch Phillips 
72a6258684SMitch Phillips   // Records the given allocation metadata into this struct.
733d8823b8SMitch Phillips   void RecordAllocation(uintptr_t Addr, size_t RequestedSize);
74a6258684SMitch Phillips   // Record that this allocation is now deallocated.
75a6258684SMitch Phillips   void RecordDeallocation();
76a6258684SMitch Phillips 
77a6258684SMitch Phillips   struct CallSiteInfo {
78a6258684SMitch Phillips     // Record the current backtrace to this callsite.
79a6258684SMitch Phillips     void RecordBacktrace(options::Backtrace_t Backtrace);
80a6258684SMitch Phillips 
81a6258684SMitch Phillips     // The compressed backtrace to the allocation/deallocation.
82a6258684SMitch Phillips     uint8_t CompressedTrace[kStackFrameStorageBytes];
83a6258684SMitch Phillips     // The thread ID for this trace, or kInvalidThreadID if not available.
84a6258684SMitch Phillips     uint64_t ThreadID = kInvalidThreadID;
85a6258684SMitch Phillips     // The size of the compressed trace (in bytes). Zero indicates that no
86a6258684SMitch Phillips     // trace was collected.
87a6258684SMitch Phillips     size_t TraceSize = 0;
88a6258684SMitch Phillips   };
89a6258684SMitch Phillips 
90a6258684SMitch Phillips   // The address of this allocation. If zero, the rest of this struct isn't
91a6258684SMitch Phillips   // valid, as the allocation has never occurred.
92a6258684SMitch Phillips   uintptr_t Addr = 0;
93a6258684SMitch Phillips   // Represents the actual size of the allocation.
943d8823b8SMitch Phillips   size_t RequestedSize = 0;
95a6258684SMitch Phillips 
96a6258684SMitch Phillips   CallSiteInfo AllocationTrace;
97a6258684SMitch Phillips   CallSiteInfo DeallocationTrace;
98a6258684SMitch Phillips 
99a6258684SMitch Phillips   // Whether this allocation has been deallocated yet.
100a6258684SMitch Phillips   bool IsDeallocated = false;
101*35b5499dSMitch Phillips 
102*35b5499dSMitch Phillips   // In recoverable mode, whether this allocation has had a crash associated
103*35b5499dSMitch Phillips   // with it. This has certain side effects, like meaning this allocation will
104*35b5499dSMitch Phillips   // permanently occupy a slot, and won't ever have another crash reported from
105*35b5499dSMitch Phillips   // it.
106*35b5499dSMitch Phillips   bool HasCrashed = false;
107a6258684SMitch Phillips };
108a6258684SMitch Phillips 
109a6258684SMitch Phillips // This holds the state that's shared between the GWP-ASan allocator and the
110a6258684SMitch Phillips // crash handler. This, in conjunction with the Metadata array, forms the entire
111a6258684SMitch Phillips // set of information required for understanding a GWP-ASan crash.
112a6258684SMitch Phillips struct AllocatorState {
AllocatorStateAllocatorState113e78b64dfSMitch Phillips   constexpr AllocatorState() {}
11404f59133SKostya Kortchinsky   AllocatorVersionMagic VersionMagic{};
115e78b64dfSMitch Phillips 
116a6258684SMitch Phillips   // Returns whether the provided pointer is a current sampled allocation that
117a6258684SMitch Phillips   // is owned by this pool.
pointerIsMineAllocatorState118a6258684SMitch Phillips   GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
119a6258684SMitch Phillips     uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
120a6258684SMitch Phillips     return P < GuardedPagePoolEnd && GuardedPagePool <= P;
121a6258684SMitch Phillips   }
122a6258684SMitch Phillips 
123a6258684SMitch Phillips   // Returns the address of the N-th guarded slot.
124a6258684SMitch Phillips   uintptr_t slotToAddr(size_t N) const;
125a6258684SMitch Phillips 
126a6258684SMitch Phillips   // Returns the largest allocation that is supported by this pool.
127a6258684SMitch Phillips   size_t maximumAllocationSize() const;
128a6258684SMitch Phillips 
129a6258684SMitch Phillips   // Gets the nearest slot to the provided address.
130a6258684SMitch Phillips   size_t getNearestSlot(uintptr_t Ptr) const;
131a6258684SMitch Phillips 
132a6258684SMitch Phillips   // Returns whether the provided pointer is a guard page or not. The pointer
133a6258684SMitch Phillips   // must be within memory owned by this pool, else the result is undefined.
134a6258684SMitch Phillips   bool isGuardPage(uintptr_t Ptr) const;
135a6258684SMitch Phillips 
136*35b5499dSMitch Phillips   // Returns the address that's used by __gwp_asan_get_internal_crash_address()
137*35b5499dSMitch Phillips   // and GPA::raiseInternallyDetectedError() to communicate that the SEGV in
138*35b5499dSMitch Phillips   // question comes from an internally-detected error.
139*35b5499dSMitch Phillips   uintptr_t internallyDetectedErrorFaultAddress() const;
140*35b5499dSMitch Phillips 
141a6258684SMitch Phillips   // The number of guarded slots that this pool holds.
142a6258684SMitch Phillips   size_t MaxSimultaneousAllocations = 0;
143a6258684SMitch Phillips 
144a6258684SMitch Phillips   // Pointer to the pool of guarded slots. Note that this points to the start of
145a6258684SMitch Phillips   // the pool (which is a guard page), not a pointer to the first guarded page.
146a6258684SMitch Phillips   uintptr_t GuardedPagePool = 0;
147a6258684SMitch Phillips   uintptr_t GuardedPagePoolEnd = 0;
148a6258684SMitch Phillips 
149a6258684SMitch Phillips   // Cached page size for this system in bytes.
150a6258684SMitch Phillips   size_t PageSize = 0;
151a6258684SMitch Phillips 
152a6258684SMitch Phillips   // The type and address of an internally-detected failure. For INVALID_FREE
153a6258684SMitch Phillips   // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set
154a6258684SMitch Phillips   // these values and terminate the process.
155a6258684SMitch Phillips   Error FailureType = Error::UNKNOWN;
156a6258684SMitch Phillips   uintptr_t FailureAddress = 0;
157a6258684SMitch Phillips };
158a6258684SMitch Phillips 
1598e167f66SMitch Phillips // Below are various compile-time checks that the layout of the internal
1608e167f66SMitch Phillips // GWP-ASan structures are undisturbed. If they are disturbed, the version magic
1618e167f66SMitch Phillips // number needs to be increased by one, and the asserts need to be updated.
1628e167f66SMitch Phillips // Out-of-process crash handlers, like breakpad/crashpad, may copy the internal
1638e167f66SMitch Phillips // GWP-ASan structures into a minidump for offline reconstruction of the crash.
1648e167f66SMitch Phillips // In order to accomplish this, the offline reconstructor needs to know the
1658e167f66SMitch Phillips // version of GWP-ASan internal structures that it's unpacking (along with the
1668e167f66SMitch Phillips // architecture-specific layout info, which is left as an exercise to the crash
1678e167f66SMitch Phillips // handler).
1688e167f66SMitch Phillips static_assert(offsetof(AllocatorState, VersionMagic) == 0, "");
1698e167f66SMitch Phillips static_assert(sizeof(AllocatorVersionMagic) == 8, "");
1708e167f66SMitch Phillips #if defined(__x86_64__)
1718e167f66SMitch Phillips static_assert(sizeof(AllocatorState) == 56, "");
1728e167f66SMitch Phillips static_assert(offsetof(AllocatorState, FailureAddress) == 48, "");
1738e167f66SMitch Phillips static_assert(sizeof(AllocationMetadata) == 568, "");
1748e167f66SMitch Phillips static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "");
1758e167f66SMitch Phillips #elif defined(__aarch64__)
1768e167f66SMitch Phillips static_assert(sizeof(AllocatorState) == 56, "");
1778e167f66SMitch Phillips static_assert(offsetof(AllocatorState, FailureAddress) == 48, "");
1788e167f66SMitch Phillips static_assert(sizeof(AllocationMetadata) == 568, "");
1798e167f66SMitch Phillips static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "");
1808e167f66SMitch Phillips #elif defined(__i386__)
1818e167f66SMitch Phillips static_assert(sizeof(AllocatorState) == 32, "");
1828e167f66SMitch Phillips static_assert(offsetof(AllocatorState, FailureAddress) == 28, "");
1838e167f66SMitch Phillips static_assert(sizeof(AllocationMetadata) == 548, "");
1848e167f66SMitch Phillips static_assert(offsetof(AllocationMetadata, IsDeallocated) == 544, "");
1858e167f66SMitch Phillips #elif defined(__arm__)
1868e167f66SMitch Phillips static_assert(sizeof(AllocatorState) == 32, "");
1878e167f66SMitch Phillips static_assert(offsetof(AllocatorState, FailureAddress) == 28, "");
1888e167f66SMitch Phillips static_assert(sizeof(AllocationMetadata) == 560, "");
1898e167f66SMitch Phillips static_assert(offsetof(AllocationMetadata, IsDeallocated) == 552, "");
1908e167f66SMitch Phillips #endif // defined($ARCHITECTURE)
1918e167f66SMitch Phillips 
192a6258684SMitch Phillips } // namespace gwp_asan
193a6258684SMitch Phillips #endif // GWP_ASAN_COMMON_H_
194