Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 1 | //===-- hwasan_checks.h -----------------------------------------*- C++ -*-===// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 | [diff] [blame] | 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 |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is a part of HWAddressSanitizer. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef HWASAN_CHECKS_H |
| 14 | #define HWASAN_CHECKS_H |
| 15 | |
Matt Morehouse | 3e4faf0 | 2021-03-23 18:57:12 | [diff] [blame] | 16 | #include "hwasan_allocator.h" |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 17 | #include "hwasan_mapping.h" |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 18 | #include "hwasan_registers.h" |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 19 | #include "sanitizer_common/sanitizer_common.h" |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 20 | |
| 21 | namespace __hwasan { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 22 | |
| 23 | enum class ErrorAction { Abort, Recover }; |
| 24 | enum class AccessType { Load, Store }; |
| 25 | |
| 26 | // Used when the access size is known. |
| 27 | constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT, |
| 28 | unsigned LogSize) { |
| 29 | return 0x20 * (EA == ErrorAction::Recover) + |
| 30 | 0x10 * (AT == AccessType::Store) + LogSize; |
| 31 | } |
| 32 | |
| 33 | // Used when the access size varies at runtime. |
| 34 | constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT) { |
| 35 | return SigTrapEncoding(EA, AT, 0xf); |
| 36 | } |
| 37 | |
| 38 | template <ErrorAction EA, AccessType AT, size_t LogSize> |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 39 | __attribute__((always_inline)) static void SigTrap(uptr p) { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 40 | // Other platforms like linux can use signals for intercepting an exception |
| 41 | // and dispatching to HandleTagMismatch. The fuchsias implementation doesn't |
| 42 | // use signals so we can call it here directly instead. |
| 43 | #if CAN_GET_REGISTERS && SANITIZER_FUCHSIA |
| 44 | auto regs = GetRegisters(); |
| 45 | size_t size = 2 << LogSize; |
| 46 | AccessInfo access_info = { |
| 47 | .addr = p, |
| 48 | .size = size, |
| 49 | .is_store = AT == AccessType::Store, |
| 50 | .is_load = AT == AccessType::Load, |
| 51 | .recover = EA == ErrorAction::Recover, |
| 52 | }; |
| 53 | HandleTagMismatch(access_info, (uptr)__builtin_return_address(0), |
| 54 | (uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x); |
| 55 | #elif defined(__aarch64__) |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 56 | (void)p; |
| 57 | // 0x900 is added to do not interfere with the kernel use of lower values of |
| 58 | // brk immediate. |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 59 | register uptr x0 asm("x0") = p; |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 60 | asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + SigTrapEncoding(EA, AT, LogSize))); |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 61 | #elif defined(__x86_64__) |
| 62 | // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes |
| 63 | // total. The pointer is passed via rdi. |
| 64 | // 0x40 is added as a safeguard, to help distinguish our trap from others and |
| 65 | // to avoid 0 offsets in the command (otherwise it'll be reduced to a |
| 66 | // different nop command, the three bytes one). |
| 67 | asm volatile( |
| 68 | "int3\n" |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 69 | "nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT, LogSize)), |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 70 | "D"(p)); |
Alexey Baturo | 38b04fd | 2022-07-31 10:38:36 | [diff] [blame] | 71 | #elif SANITIZER_RISCV64 |
| 72 | // Put pointer into x10 |
| 73 | // addiw contains immediate of 0x40 + X, where 0x40 is magic number and X |
| 74 | // encodes access size |
| 75 | register uptr x10 asm("x10") = p; |
| 76 | asm volatile( |
| 77 | "ebreak\n" |
| 78 | "addiw x0, x0, %1\n" ::"r"(x10), |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 79 | "I"(0x40 + SigTrapEncoding(EA, AT, LogSize))); |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 80 | #else |
| 81 | // FIXME: not always sigill. |
| 82 | __builtin_trap(); |
| 83 | #endif |
| 84 | // __builtin_unreachable(); |
| 85 | } |
| 86 | |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 87 | // Version with access size which is not power of 2 |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 88 | template <ErrorAction EA, AccessType AT> |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 89 | __attribute__((always_inline)) static void SigTrap(uptr p, uptr size) { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 90 | // Other platforms like linux can use signals for intercepting an exception |
| 91 | // and dispatching to HandleTagMismatch. The fuchsias implementation doesn't |
| 92 | // use signals so we can call it here directly instead. |
| 93 | #if CAN_GET_REGISTERS && SANITIZER_FUCHSIA |
| 94 | auto regs = GetRegisters(); |
| 95 | AccessInfo access_info = { |
| 96 | .addr = p, |
| 97 | .size = size, |
| 98 | .is_store = AT == AccessType::Store, |
| 99 | .is_load = AT == AccessType::Load, |
| 100 | .recover = EA == ErrorAction::Recover, |
| 101 | }; |
| 102 | HandleTagMismatch(access_info, (uptr)__builtin_return_address(0), |
| 103 | (uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x); |
| 104 | #elif defined(__aarch64__) |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 105 | register uptr x0 asm("x0") = p; |
| 106 | register uptr x1 asm("x1") = size; |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 107 | asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + SigTrapEncoding(EA, AT))); |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 108 | #elif defined(__x86_64__) |
| 109 | // Size is stored in rsi. |
| 110 | asm volatile( |
| 111 | "int3\n" |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 112 | "nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT)), |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 113 | "D"(p), "S"(size)); |
Alexey Baturo | 38b04fd | 2022-07-31 10:38:36 | [diff] [blame] | 114 | #elif SANITIZER_RISCV64 |
| 115 | // Put access size into x11 |
| 116 | register uptr x10 asm("x10") = p; |
| 117 | register uptr x11 asm("x11") = size; |
| 118 | asm volatile( |
| 119 | "ebreak\n" |
| 120 | "addiw x0, x0, %2\n" ::"r"(x10), |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 121 | "r"(x11), "I"(0x40 + SigTrapEncoding(EA, AT))); |
Eugene Leviant | 0d7952c | 2019-01-21 09:51:10 | [diff] [blame] | 122 | #else |
| 123 | __builtin_trap(); |
| 124 | #endif |
| 125 | // __builtin_unreachable(); |
| 126 | } |
| 127 | |
Vitaly Buka | 5a5bf05 | 2023-04-29 01:18:31 | [diff] [blame] | 128 | __attribute__((always_inline, nodebug)) static inline uptr ShortTagSize( |
| 129 | tag_t mem_tag, uptr ptr) { |
| 130 | DCHECK(IsAligned(ptr, kShadowAlignment)); |
| 131 | tag_t ptr_tag = GetTagFromPointer(ptr); |
| 132 | if (ptr_tag == mem_tag) |
| 133 | return kShadowAlignment; |
| 134 | if (!mem_tag || mem_tag >= kShadowAlignment) |
| 135 | return 0; |
| 136 | if (*(u8 *)(ptr | (kShadowAlignment - 1)) != ptr_tag) |
| 137 | return 0; |
| 138 | return mem_tag; |
| 139 | } |
| 140 | |
Thurston Dang | 81d0626 | 2023-04-28 02:50:09 | [diff] [blame] | 141 | __attribute__((always_inline, nodebug)) static inline bool |
| 142 | PossiblyShortTagMatches(tag_t mem_tag, uptr ptr, uptr sz) { |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 143 | tag_t ptr_tag = GetTagFromPointer(ptr); |
| 144 | if (ptr_tag == mem_tag) |
| 145 | return true; |
| 146 | if (mem_tag >= kShadowAlignment) |
| 147 | return false; |
| 148 | if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag) |
| 149 | return false; |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 150 | return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag; |
| 151 | } |
| 152 | |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 153 | template <ErrorAction EA, AccessType AT, unsigned LogSize> |
| 154 | __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) { |
Matt Morehouse | 3e4faf0 | 2021-03-23 18:57:12 | [diff] [blame] | 155 | if (!InTaggableRegion(p)) |
| 156 | return; |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 157 | uptr ptr_raw = p & ~kAddressTagMask; |
| 158 | tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw); |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 159 | if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 160 | SigTrap<EA, AT, LogSize>(p); |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 161 | if (EA == ErrorAction::Abort) |
| 162 | __builtin_unreachable(); |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | template <ErrorAction EA, AccessType AT> |
| 167 | __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p, |
| 168 | uptr sz) { |
Matt Morehouse | 3e4faf0 | 2021-03-23 18:57:12 | [diff] [blame] | 169 | if (sz == 0 || !InTaggableRegion(p)) |
Peter Collingbourne | fdef020 | 2019-01-09 00:44:13 | [diff] [blame] | 170 | return; |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 171 | tag_t ptr_tag = GetTagFromPointer(p); |
| 172 | uptr ptr_raw = p & ~kAddressTagMask; |
| 173 | tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw); |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 174 | tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz); |
| 175 | for (tag_t *t = shadow_first; t < shadow_last; ++t) |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 176 | if (UNLIKELY(ptr_tag != *t)) { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 177 | SigTrap<EA, AT>(p, sz); |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 178 | if (EA == ErrorAction::Abort) |
| 179 | __builtin_unreachable(); |
| 180 | } |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 181 | uptr end = p + sz; |
Vitaly Buka | fc75c27 | 2023-04-28 07:44:46 | [diff] [blame] | 182 | uptr tail_sz = end & (kShadowAlignment - 1); |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 183 | if (UNLIKELY(tail_sz != 0 && |
| 184 | !PossiblyShortTagMatches( |
| 185 | *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) { |
Leonard Chan | 913b4aa | 2023-02-14 19:59:15 | [diff] [blame] | 186 | SigTrap<EA, AT>(p, sz); |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 187 | if (EA == ErrorAction::Abort) |
| 188 | __builtin_unreachable(); |
| 189 | } |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 190 | } |
Peter Collingbourne | 1366262 | 2019-07-09 20:22:36 | [diff] [blame] | 191 | |
Eugene Leviant | d3bd614 | 2018-12-20 09:10:03 | [diff] [blame] | 192 | } // end namespace __hwasan |
| 193 | |
| 194 | #endif // HWASAN_CHECKS_H |