xref: /openbsd-src/gnu/llvm/compiler-rt/lib/gwp_asan/common.h (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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