1 | //===-- hwasan.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 is a part of HWAddressSanitizer. |
10 | // |
11 | // Private Hwasan header. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef HWASAN_H |
15 | #define HWASAN_H |
16 | |
17 | #include "hwasan_flags.h" |
18 | #include "hwasan_interface_internal.h" |
19 | #include "hwasan_mapping.h" |
20 | #include "sanitizer_common/sanitizer_common.h" |
21 | #include "sanitizer_common/sanitizer_flags.h" |
22 | #include "sanitizer_common/sanitizer_internal_defs.h" |
23 | #include "sanitizer_common/sanitizer_stacktrace.h" |
24 | #include "ubsan/ubsan_platform.h" |
25 | |
26 | #ifndef HWASAN_CONTAINS_UBSAN |
27 | # define HWASAN_CONTAINS_UBSAN CAN_SANITIZE_UB |
28 | #endif |
29 | |
30 | #ifndef HWASAN_WITH_INTERCEPTORS |
31 | #define HWASAN_WITH_INTERCEPTORS 0 |
32 | #endif |
33 | |
34 | #ifndef HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE |
35 | #define HWASAN_REPLACE_OPERATORS_NEW_AND_DELETE HWASAN_WITH_INTERCEPTORS |
36 | #endif |
37 | |
38 | typedef u8 tag_t; |
39 | |
40 | #if defined(HWASAN_ALIASING_MODE) |
41 | # if !defined(__x86_64__) |
42 | # error Aliasing mode is only supported on x86_64 |
43 | # endif |
44 | // Tags are done in middle bits using userspace aliasing. |
45 | constexpr unsigned kAddressTagShift = 39; |
46 | constexpr unsigned kTagBits = 3; |
47 | |
48 | // The alias region is placed next to the shadow so the upper bits of all |
49 | // taggable addresses matches the upper bits of the shadow base. This shift |
50 | // value determines which upper bits must match. It has a floor of 44 since the |
51 | // shadow is always 8TB. |
52 | // TODO(morehouse): In alias mode we can shrink the shadow and use a |
53 | // simpler/faster shadow calculation. |
54 | constexpr unsigned kTaggableRegionCheckShift = |
55 | __sanitizer::Max(kAddressTagShift + kTagBits + 1U, 44U); |
56 | #elif defined(__x86_64__) |
57 | // Tags are done in upper bits using Intel LAM. |
58 | constexpr unsigned kAddressTagShift = 57; |
59 | constexpr unsigned kTagBits = 6; |
60 | #else |
61 | // TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address |
62 | // translation and can be used to store a tag. |
63 | constexpr unsigned kAddressTagShift = 56; |
64 | constexpr unsigned kTagBits = 8; |
65 | #endif // defined(HWASAN_ALIASING_MODE) |
66 | |
67 | // Mask for extracting tag bits from the lower 8 bits. |
68 | constexpr uptr kTagMask = (1UL << kTagBits) - 1; |
69 | |
70 | // Mask for extracting tag bits from full pointers. |
71 | constexpr uptr kAddressTagMask = kTagMask << kAddressTagShift; |
72 | |
73 | // Minimal alignment of the shadow base address. Determines the space available |
74 | // for threads and stack histories. This is an ABI constant. |
75 | const unsigned kShadowBaseAlignment = 32; |
76 | |
77 | const unsigned kRecordAddrBaseTagShift = 3; |
78 | const unsigned kRecordFPShift = 48; |
79 | const unsigned kRecordFPLShift = 4; |
80 | const unsigned kRecordFPModulus = 1 << (64 - kRecordFPShift + kRecordFPLShift); |
81 | |
82 | static inline bool InTaggableRegion(uptr addr) { |
83 | #if defined(HWASAN_ALIASING_MODE) |
84 | // Aliases are mapped next to shadow so that the upper bits match the shadow |
85 | // base. |
86 | return (addr >> kTaggableRegionCheckShift) == |
87 | (__hwasan::GetShadowOffset() >> kTaggableRegionCheckShift); |
88 | #endif |
89 | return true; |
90 | } |
91 | |
92 | static inline tag_t GetTagFromPointer(uptr p) { |
93 | return InTaggableRegion(addr: p) ? ((p >> kAddressTagShift) & kTagMask) : 0; |
94 | } |
95 | |
96 | static inline uptr UntagAddr(uptr tagged_addr) { |
97 | return InTaggableRegion(addr: tagged_addr) ? (tagged_addr & ~kAddressTagMask) |
98 | : tagged_addr; |
99 | } |
100 | |
101 | static inline void *UntagPtr(const void *tagged_ptr) { |
102 | return reinterpret_cast<void *>( |
103 | UntagAddr(tagged_addr: reinterpret_cast<uptr>(tagged_ptr))); |
104 | } |
105 | |
106 | static inline uptr AddTagToPointer(uptr p, tag_t tag) { |
107 | return InTaggableRegion(addr: p) ? ((p & ~kAddressTagMask) | |
108 | ((uptr)(tag & kTagMask) << kAddressTagShift)) |
109 | : p; |
110 | } |
111 | |
112 | namespace __hwasan { |
113 | |
114 | extern int hwasan_inited; |
115 | extern bool hwasan_init_is_running; |
116 | extern int hwasan_report_count; |
117 | |
118 | bool InitShadow(); |
119 | void InitializeOsSupport(); |
120 | void InitThreads(); |
121 | void InitializeInterceptors(); |
122 | |
123 | void HwasanAllocatorInit(); |
124 | void HwasanAllocatorLock(); |
125 | void HwasanAllocatorUnlock(); |
126 | |
127 | void *hwasan_malloc(uptr size, StackTrace *stack); |
128 | void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack); |
129 | void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack); |
130 | void *hwasan_reallocarray(void *ptr, uptr nmemb, uptr size, StackTrace *stack); |
131 | void *hwasan_valloc(uptr size, StackTrace *stack); |
132 | void *hwasan_pvalloc(uptr size, StackTrace *stack); |
133 | void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack); |
134 | void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack); |
135 | int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size, |
136 | StackTrace *stack); |
137 | void hwasan_free(void *ptr, StackTrace *stack); |
138 | |
139 | void InstallAtExitHandler(); |
140 | |
141 | #define GET_MALLOC_STACK_TRACE \ |
142 | BufferedStackTrace stack; \ |
143 | if (hwasan_inited) \ |
144 | stack.Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ |
145 | nullptr, common_flags()->fast_unwind_on_malloc, \ |
146 | common_flags()->malloc_context_size) |
147 | |
148 | #define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \ |
149 | BufferedStackTrace stack; \ |
150 | if (hwasan_inited) \ |
151 | stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal) |
152 | |
153 | void HwasanTSDInit(); |
154 | void HwasanTSDThreadInit(); |
155 | void HwasanAtExit(); |
156 | |
157 | void HwasanOnDeadlySignal(int signo, void *info, void *context); |
158 | |
159 | void HwasanInstallAtForkHandler(); |
160 | |
161 | void InstallAtExitCheckLeaks(); |
162 | |
163 | void UpdateMemoryUsage(); |
164 | |
165 | void AppendToErrorMessageBuffer(const char *buffer); |
166 | |
167 | void AndroidTestTlsSlot(); |
168 | |
169 | // This is a compiler-generated struct that can be shared between hwasan |
170 | // implementations. |
171 | struct AccessInfo { |
172 | uptr addr; |
173 | uptr size; |
174 | bool is_store; |
175 | bool is_load; |
176 | bool recover; |
177 | }; |
178 | |
179 | // Given access info and frame information, unwind the stack and report the tag |
180 | // mismatch. |
181 | void HandleTagMismatch(AccessInfo ai, uptr pc, uptr frame, void *uc, |
182 | uptr *registers_frame = nullptr); |
183 | |
184 | // This dispatches to HandleTagMismatch but sets up the AccessInfo, program |
185 | // counter, and frame pointer. |
186 | void HwasanTagMismatch(uptr addr, uptr pc, uptr frame, uptr access_info, |
187 | uptr *registers_frame, size_t outsize); |
188 | |
189 | } // namespace __hwasan |
190 | |
191 | #if HWASAN_WITH_INTERCEPTORS |
192 | // For both bionic and glibc __sigset_t is an unsigned long. |
193 | typedef unsigned long __hw_sigset_t; |
194 | // Setjmp and longjmp implementations are platform specific, and hence the |
195 | // interception code is platform specific too. |
196 | # if defined(__aarch64__) |
197 | constexpr size_t kHwRegisterBufSize = 22; |
198 | # elif defined(__x86_64__) |
199 | constexpr size_t kHwRegisterBufSize = 8; |
200 | # elif SANITIZER_RISCV64 |
201 | // saving PC, 12 int regs, sp, 12 fp regs |
202 | # ifndef __riscv_float_abi_soft |
203 | constexpr size_t kHwRegisterBufSize = 1 + 12 + 1 + 12; |
204 | # else |
205 | constexpr size_t kHwRegisterBufSize = 1 + 12 + 1; |
206 | # endif |
207 | # endif |
208 | typedef unsigned long long __hw_register_buf[kHwRegisterBufSize]; |
209 | struct __hw_jmp_buf_struct { |
210 | // NOTE: The machine-dependent definition of `__sigsetjmp' |
211 | // assume that a `__hw_jmp_buf' begins with a `__hw_register_buf' and that |
212 | // `__mask_was_saved' follows it. Do not move these members or add others |
213 | // before it. |
214 | // |
215 | // We add a __magic field to our struct to catch cases where libc's setjmp |
216 | // populated the jmp_buf instead of our interceptor. |
217 | __hw_register_buf __jmpbuf; // Calling environment. |
218 | unsigned __mask_was_saved : 1; // Saved the signal mask? |
219 | unsigned __magic : 31; // Used to distinguish __hw_jmp_buf from jmp_buf. |
220 | __hw_sigset_t __saved_mask; // Saved signal mask. |
221 | }; |
222 | typedef struct __hw_jmp_buf_struct __hw_jmp_buf[1]; |
223 | typedef struct __hw_jmp_buf_struct __hw_sigjmp_buf[1]; |
224 | constexpr unsigned kHwJmpBufMagic = 0x248ACE77; |
225 | #endif // HWASAN_WITH_INTERCEPTORS |
226 | |
227 | #define ENSURE_HWASAN_INITED() \ |
228 | do { \ |
229 | CHECK(!hwasan_init_is_running); \ |
230 | if (!hwasan_inited) { \ |
231 | __hwasan_init(); \ |
232 | } \ |
233 | } while (0) |
234 | |
235 | #endif // HWASAN_H |
236 | |