xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/gwp_asan/common.h (revision 5ffd83dbcc34f10e07f6d3e968ae6365869615f4)
1*5ffd83dbSDimitry Andric //===-- common.h ------------------------------------------------*- C++ -*-===//
2*5ffd83dbSDimitry Andric //
3*5ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*5ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*5ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*5ffd83dbSDimitry Andric //
7*5ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
8*5ffd83dbSDimitry Andric 
9*5ffd83dbSDimitry Andric // This file contains code that is common between the crash handler and the
10*5ffd83dbSDimitry Andric // GuardedPoolAllocator.
11*5ffd83dbSDimitry Andric 
12*5ffd83dbSDimitry Andric #ifndef GWP_ASAN_COMMON_H_
13*5ffd83dbSDimitry Andric #define GWP_ASAN_COMMON_H_
14*5ffd83dbSDimitry Andric 
15*5ffd83dbSDimitry Andric #include "gwp_asan/definitions.h"
16*5ffd83dbSDimitry Andric #include "gwp_asan/options.h"
17*5ffd83dbSDimitry Andric 
18*5ffd83dbSDimitry Andric #include <stddef.h>
19*5ffd83dbSDimitry Andric #include <stdint.h>
20*5ffd83dbSDimitry Andric 
21*5ffd83dbSDimitry Andric namespace gwp_asan {
22*5ffd83dbSDimitry Andric enum class Error {
23*5ffd83dbSDimitry Andric   UNKNOWN,
24*5ffd83dbSDimitry Andric   USE_AFTER_FREE,
25*5ffd83dbSDimitry Andric   DOUBLE_FREE,
26*5ffd83dbSDimitry Andric   INVALID_FREE,
27*5ffd83dbSDimitry Andric   BUFFER_OVERFLOW,
28*5ffd83dbSDimitry Andric   BUFFER_UNDERFLOW
29*5ffd83dbSDimitry Andric };
30*5ffd83dbSDimitry Andric 
31*5ffd83dbSDimitry Andric const char *ErrorToString(const Error &E);
32*5ffd83dbSDimitry Andric 
33*5ffd83dbSDimitry Andric static constexpr uint64_t kInvalidThreadID = UINT64_MAX;
34*5ffd83dbSDimitry Andric // Get the current thread ID, or kInvalidThreadID if failure. Note: This
35*5ffd83dbSDimitry Andric // implementation is platform-specific.
36*5ffd83dbSDimitry Andric uint64_t getThreadID();
37*5ffd83dbSDimitry Andric 
38*5ffd83dbSDimitry Andric // This struct contains all the metadata recorded about a single allocation made
39*5ffd83dbSDimitry Andric // by GWP-ASan. If `AllocationMetadata.Addr` is zero, the metadata is non-valid.
40*5ffd83dbSDimitry Andric struct AllocationMetadata {
41*5ffd83dbSDimitry Andric   // The number of bytes used to store a compressed stack frame. On 64-bit
42*5ffd83dbSDimitry Andric   // platforms, assuming a compression ratio of 50%, this should allow us to
43*5ffd83dbSDimitry Andric   // store ~64 frames per trace.
44*5ffd83dbSDimitry Andric   static constexpr size_t kStackFrameStorageBytes = 256;
45*5ffd83dbSDimitry Andric 
46*5ffd83dbSDimitry Andric   // Maximum number of stack frames to collect on allocation/deallocation. The
47*5ffd83dbSDimitry Andric   // actual number of collected frames may be less than this as the stack
48*5ffd83dbSDimitry Andric   // frames are compressed into a fixed memory range.
49*5ffd83dbSDimitry Andric   static constexpr size_t kMaxTraceLengthToCollect = 128;
50*5ffd83dbSDimitry Andric 
51*5ffd83dbSDimitry Andric   // Records the given allocation metadata into this struct.
52*5ffd83dbSDimitry Andric   void RecordAllocation(uintptr_t Addr, size_t Size);
53*5ffd83dbSDimitry Andric   // Record that this allocation is now deallocated.
54*5ffd83dbSDimitry Andric   void RecordDeallocation();
55*5ffd83dbSDimitry Andric 
56*5ffd83dbSDimitry Andric   struct CallSiteInfo {
57*5ffd83dbSDimitry Andric     // Record the current backtrace to this callsite.
58*5ffd83dbSDimitry Andric     void RecordBacktrace(options::Backtrace_t Backtrace);
59*5ffd83dbSDimitry Andric 
60*5ffd83dbSDimitry Andric     // The compressed backtrace to the allocation/deallocation.
61*5ffd83dbSDimitry Andric     uint8_t CompressedTrace[kStackFrameStorageBytes];
62*5ffd83dbSDimitry Andric     // The thread ID for this trace, or kInvalidThreadID if not available.
63*5ffd83dbSDimitry Andric     uint64_t ThreadID = kInvalidThreadID;
64*5ffd83dbSDimitry Andric     // The size of the compressed trace (in bytes). Zero indicates that no
65*5ffd83dbSDimitry Andric     // trace was collected.
66*5ffd83dbSDimitry Andric     size_t TraceSize = 0;
67*5ffd83dbSDimitry Andric   };
68*5ffd83dbSDimitry Andric 
69*5ffd83dbSDimitry Andric   // The address of this allocation. If zero, the rest of this struct isn't
70*5ffd83dbSDimitry Andric   // valid, as the allocation has never occurred.
71*5ffd83dbSDimitry Andric   uintptr_t Addr = 0;
72*5ffd83dbSDimitry Andric   // Represents the actual size of the allocation.
73*5ffd83dbSDimitry Andric   size_t Size = 0;
74*5ffd83dbSDimitry Andric 
75*5ffd83dbSDimitry Andric   CallSiteInfo AllocationTrace;
76*5ffd83dbSDimitry Andric   CallSiteInfo DeallocationTrace;
77*5ffd83dbSDimitry Andric 
78*5ffd83dbSDimitry Andric   // Whether this allocation has been deallocated yet.
79*5ffd83dbSDimitry Andric   bool IsDeallocated = false;
80*5ffd83dbSDimitry Andric };
81*5ffd83dbSDimitry Andric 
82*5ffd83dbSDimitry Andric // This holds the state that's shared between the GWP-ASan allocator and the
83*5ffd83dbSDimitry Andric // crash handler. This, in conjunction with the Metadata array, forms the entire
84*5ffd83dbSDimitry Andric // set of information required for understanding a GWP-ASan crash.
85*5ffd83dbSDimitry Andric struct AllocatorState {
86*5ffd83dbSDimitry Andric   // Returns whether the provided pointer is a current sampled allocation that
87*5ffd83dbSDimitry Andric   // is owned by this pool.
88*5ffd83dbSDimitry Andric   GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
89*5ffd83dbSDimitry Andric     uintptr_t P = reinterpret_cast<uintptr_t>(Ptr);
90*5ffd83dbSDimitry Andric     return P < GuardedPagePoolEnd && GuardedPagePool <= P;
91*5ffd83dbSDimitry Andric   }
92*5ffd83dbSDimitry Andric 
93*5ffd83dbSDimitry Andric   // Returns the address of the N-th guarded slot.
94*5ffd83dbSDimitry Andric   uintptr_t slotToAddr(size_t N) const;
95*5ffd83dbSDimitry Andric 
96*5ffd83dbSDimitry Andric   // Returns the largest allocation that is supported by this pool.
97*5ffd83dbSDimitry Andric   size_t maximumAllocationSize() const;
98*5ffd83dbSDimitry Andric 
99*5ffd83dbSDimitry Andric   // Gets the nearest slot to the provided address.
100*5ffd83dbSDimitry Andric   size_t getNearestSlot(uintptr_t Ptr) const;
101*5ffd83dbSDimitry Andric 
102*5ffd83dbSDimitry Andric   // Returns whether the provided pointer is a guard page or not. The pointer
103*5ffd83dbSDimitry Andric   // must be within memory owned by this pool, else the result is undefined.
104*5ffd83dbSDimitry Andric   bool isGuardPage(uintptr_t Ptr) const;
105*5ffd83dbSDimitry Andric 
106*5ffd83dbSDimitry Andric   // The number of guarded slots that this pool holds.
107*5ffd83dbSDimitry Andric   size_t MaxSimultaneousAllocations = 0;
108*5ffd83dbSDimitry Andric 
109*5ffd83dbSDimitry Andric   // Pointer to the pool of guarded slots. Note that this points to the start of
110*5ffd83dbSDimitry Andric   // the pool (which is a guard page), not a pointer to the first guarded page.
111*5ffd83dbSDimitry Andric   uintptr_t GuardedPagePool = 0;
112*5ffd83dbSDimitry Andric   uintptr_t GuardedPagePoolEnd = 0;
113*5ffd83dbSDimitry Andric 
114*5ffd83dbSDimitry Andric   // Cached page size for this system in bytes.
115*5ffd83dbSDimitry Andric   size_t PageSize = 0;
116*5ffd83dbSDimitry Andric 
117*5ffd83dbSDimitry Andric   // The type and address of an internally-detected failure. For INVALID_FREE
118*5ffd83dbSDimitry Andric   // and DOUBLE_FREE, these errors are detected in GWP-ASan, which will set
119*5ffd83dbSDimitry Andric   // these values and terminate the process.
120*5ffd83dbSDimitry Andric   Error FailureType = Error::UNKNOWN;
121*5ffd83dbSDimitry Andric   uintptr_t FailureAddress = 0;
122*5ffd83dbSDimitry Andric };
123*5ffd83dbSDimitry Andric 
124*5ffd83dbSDimitry Andric } // namespace gwp_asan
125*5ffd83dbSDimitry Andric #endif // GWP_ASAN_COMMON_H_
126