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, 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(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 DCHECK(IsAligned(ptr, kShadowAlignment)); 144 tag_t ptr_tag = GetTagFromPointer(ptr); 145 if (ptr_tag == mem_tag) 146 return true; 147 if (mem_tag >= kShadowAlignment) 148 return false; 149 if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag) 150 return false; 151 return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag; 152 } 153 154 template <ErrorAction EA, AccessType AT, unsigned LogSize> 155 __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) { 156 if (!InTaggableRegion(p)) 157 return; 158 uptr ptr_raw = p & ~kAddressTagMask; 159 tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw); 160 if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) { 161 SigTrap<EA, AT, LogSize>(p); 162 if (EA == ErrorAction::Abort) 163 __builtin_unreachable(); 164 } 165 } 166 167 template <ErrorAction EA, AccessType AT> 168 __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p, 169 uptr sz) { 170 if (sz == 0 || !InTaggableRegion(p)) 171 return; 172 tag_t ptr_tag = GetTagFromPointer(p); 173 uptr ptr_raw = p & ~kAddressTagMask; 174 tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw); 175 tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz); 176 for (tag_t *t = shadow_first; t < shadow_last; ++t) 177 if (UNLIKELY(ptr_tag != *t)) { 178 SigTrap<EA, AT>(p, sz); 179 if (EA == ErrorAction::Abort) 180 __builtin_unreachable(); 181 } 182 uptr end = p + sz; 183 uptr tail_sz = end & (kShadowAlignment - 1); 184 if (UNLIKELY(tail_sz != 0 && 185 !PossiblyShortTagMatches( 186 *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) { 187 SigTrap<EA, AT>(p, sz); 188 if (EA == ErrorAction::Abort) 189 __builtin_unreachable(); 190 } 191 } 192 193 } // end namespace __hwasan 194 195 #endif // HWASAN_CHECKS_H 196