| 1 | //===-- hwasan_checks.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 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #ifndef HWASAN_CHECKS_H |
| 14 | #define HWASAN_CHECKS_H |
| 15 | |
| 16 | #include "hwasan_allocator.h" |
| 17 | #include "hwasan_mapping.h" |
| 18 | #include "hwasan_registers.h" |
| 19 | #include "sanitizer_common/sanitizer_common.h" |
| 20 | |
| 21 | namespace __hwasan { |
| 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, LogSize: 0xf); |
| 36 | } |
| 37 | |
| 38 | template <ErrorAction EA, AccessType AT, size_t LogSize> |
| 39 | __attribute__((always_inline)) static void SigTrap(uptr p) { |
| 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__) |
| 56 | (void)p; |
| 57 | // 0x900 is added to do not interfere with the kernel use of lower values of |
| 58 | // brk immediate. |
| 59 | register uptr x0 asm("x0" ) = p; |
| 60 | asm("brk %1\n\t" ::"r" (x0), "n" (0x900 + SigTrapEncoding(EA, AT, LogSize))); |
| 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" |
| 69 | "nopl %c0(%%rax)\n" ::"n" (0x40 + SigTrapEncoding(EA, AT, LogSize)), |
| 70 | "D" (p)); |
| 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), |
| 79 | "I" (0x40 + SigTrapEncoding(EA, AT, LogSize))); |
| 80 | #else |
| 81 | // FIXME: not always sigill. |
| 82 | __builtin_trap(); |
| 83 | #endif |
| 84 | // __builtin_unreachable(); |
| 85 | } |
| 86 | |
| 87 | // Version with access size which is not power of 2 |
| 88 | template <ErrorAction EA, AccessType AT> |
| 89 | __attribute__((always_inline)) static void SigTrap(uptr p, uptr size) { |
| 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__) |
| 105 | register uptr x0 asm("x0" ) = p; |
| 106 | register uptr x1 asm("x1" ) = size; |
| 107 | asm("brk %2\n\t" ::"r" (x0), "r" (x1), "n" (0x900 + SigTrapEncoding(EA, AT))); |
| 108 | #elif defined(__x86_64__) |
| 109 | // Size is stored in rsi. |
| 110 | asm volatile( |
| 111 | "int3\n" |
| 112 | "nopl %c0(%%rax)\n" ::"n" (0x40 + SigTrapEncoding(EA, AT)), |
| 113 | "D" (p), "S" (size)); |
| 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), |
| 121 | "r" (x11), "I" (0x40 + SigTrapEncoding(EA, AT))); |
| 122 | #else |
| 123 | __builtin_trap(); |
| 124 | #endif |
| 125 | // __builtin_unreachable(); |
| 126 | } |
| 127 | |
| 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(p: 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 | |
| 141 | __attribute__((always_inline, nodebug)) static inline bool |
| 142 | PossiblyShortTagMatches(tag_t mem_tag, uptr ptr, uptr sz) { |
| 143 | tag_t ptr_tag = GetTagFromPointer(p: 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; |
| 150 | return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag; |
| 151 | } |
| 152 | |
| 153 | template <ErrorAction EA, AccessType AT, unsigned LogSize> |
| 154 | __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) { |
| 155 | if (!InTaggableRegion(addr: p)) |
| 156 | return; |
| 157 | uptr ptr_raw = p & ~kAddressTagMask; |
| 158 | tag_t mem_tag = *(tag_t *)MemToShadow(untagged_addr: ptr_raw); |
| 159 | if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) { |
| 160 | SigTrap<EA, AT, LogSize>(p); |
| 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) { |
| 169 | if (sz == 0 || !InTaggableRegion(addr: p)) |
| 170 | return; |
| 171 | tag_t ptr_tag = GetTagFromPointer(p); |
| 172 | uptr ptr_raw = p & ~kAddressTagMask; |
| 173 | tag_t *shadow_first = (tag_t *)MemToShadow(untagged_addr: ptr_raw); |
| 174 | tag_t *shadow_last = (tag_t *)MemToShadow(untagged_addr: ptr_raw + sz); |
| 175 | for (tag_t *t = shadow_first; t < shadow_last; ++t) |
| 176 | if (UNLIKELY(ptr_tag != *t)) { |
| 177 | SigTrap<EA, AT>(p, sz); |
| 178 | if (EA == ErrorAction::Abort) |
| 179 | __builtin_unreachable(); |
| 180 | } |
| 181 | uptr end = p + sz; |
| 182 | uptr tail_sz = end & (kShadowAlignment - 1); |
| 183 | if (UNLIKELY(tail_sz != 0 && |
| 184 | !PossiblyShortTagMatches( |
| 185 | *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) { |
| 186 | SigTrap<EA, AT>(p, sz); |
| 187 | if (EA == ErrorAction::Abort) |
| 188 | __builtin_unreachable(); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | } // end namespace __hwasan |
| 193 | |
| 194 | #endif // HWASAN_CHECKS_H |
| 195 | |