10b57cec5SDimitry Andric //===-- hwasan_checks.h -----------------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file is a part of HWAddressSanitizer.
100b57cec5SDimitry Andric //
110b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
120b57cec5SDimitry Andric
130b57cec5SDimitry Andric #ifndef HWASAN_CHECKS_H
140b57cec5SDimitry Andric #define HWASAN_CHECKS_H
150b57cec5SDimitry Andric
16fe6060f1SDimitry Andric #include "hwasan_allocator.h"
170b57cec5SDimitry Andric #include "hwasan_mapping.h"
18*06c3fb27SDimitry Andric #include "hwasan_registers.h"
190b57cec5SDimitry Andric #include "sanitizer_common/sanitizer_common.h"
200b57cec5SDimitry Andric
210b57cec5SDimitry Andric namespace __hwasan {
22*06c3fb27SDimitry Andric
23*06c3fb27SDimitry Andric enum class ErrorAction { Abort, Recover };
24*06c3fb27SDimitry Andric enum class AccessType { Load, Store };
25*06c3fb27SDimitry Andric
26*06c3fb27SDimitry Andric // Used when the access size is known.
SigTrapEncoding(ErrorAction EA,AccessType AT,unsigned LogSize)27*06c3fb27SDimitry Andric constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT,
28*06c3fb27SDimitry Andric unsigned LogSize) {
29*06c3fb27SDimitry Andric return 0x20 * (EA == ErrorAction::Recover) +
30*06c3fb27SDimitry Andric 0x10 * (AT == AccessType::Store) + LogSize;
31*06c3fb27SDimitry Andric }
32*06c3fb27SDimitry Andric
33*06c3fb27SDimitry Andric // Used when the access size varies at runtime.
SigTrapEncoding(ErrorAction EA,AccessType AT)34*06c3fb27SDimitry Andric constexpr unsigned SigTrapEncoding(ErrorAction EA, AccessType AT) {
35*06c3fb27SDimitry Andric return SigTrapEncoding(EA, AT, 0xf);
36*06c3fb27SDimitry Andric }
37*06c3fb27SDimitry Andric
38*06c3fb27SDimitry Andric template <ErrorAction EA, AccessType AT, size_t LogSize>
SigTrap(uptr p)390b57cec5SDimitry Andric __attribute__((always_inline)) static void SigTrap(uptr p) {
40*06c3fb27SDimitry Andric // Other platforms like linux can use signals for intercepting an exception
41*06c3fb27SDimitry Andric // and dispatching to HandleTagMismatch. The fuchsias implementation doesn't
42*06c3fb27SDimitry Andric // use signals so we can call it here directly instead.
43*06c3fb27SDimitry Andric #if CAN_GET_REGISTERS && SANITIZER_FUCHSIA
44*06c3fb27SDimitry Andric auto regs = GetRegisters();
45*06c3fb27SDimitry Andric size_t size = 2 << LogSize;
46*06c3fb27SDimitry Andric AccessInfo access_info = {
47*06c3fb27SDimitry Andric .addr = p,
48*06c3fb27SDimitry Andric .size = size,
49*06c3fb27SDimitry Andric .is_store = AT == AccessType::Store,
50*06c3fb27SDimitry Andric .is_load = AT == AccessType::Load,
51*06c3fb27SDimitry Andric .recover = EA == ErrorAction::Recover,
52*06c3fb27SDimitry Andric };
53*06c3fb27SDimitry Andric HandleTagMismatch(access_info, (uptr)__builtin_return_address(0),
54*06c3fb27SDimitry Andric (uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x);
55*06c3fb27SDimitry Andric #elif defined(__aarch64__)
560b57cec5SDimitry Andric (void)p;
570b57cec5SDimitry Andric // 0x900 is added to do not interfere with the kernel use of lower values of
580b57cec5SDimitry Andric // brk immediate.
590b57cec5SDimitry Andric register uptr x0 asm("x0") = p;
60*06c3fb27SDimitry Andric asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + SigTrapEncoding(EA, AT, LogSize)));
610b57cec5SDimitry Andric #elif defined(__x86_64__)
620b57cec5SDimitry Andric // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
630b57cec5SDimitry Andric // total. The pointer is passed via rdi.
640b57cec5SDimitry Andric // 0x40 is added as a safeguard, to help distinguish our trap from others and
650b57cec5SDimitry Andric // to avoid 0 offsets in the command (otherwise it'll be reduced to a
660b57cec5SDimitry Andric // different nop command, the three bytes one).
670b57cec5SDimitry Andric asm volatile(
680b57cec5SDimitry Andric "int3\n"
69*06c3fb27SDimitry Andric "nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT, LogSize)),
700b57cec5SDimitry Andric "D"(p));
71bdd1243dSDimitry Andric #elif SANITIZER_RISCV64
72bdd1243dSDimitry Andric // Put pointer into x10
73bdd1243dSDimitry Andric // addiw contains immediate of 0x40 + X, where 0x40 is magic number and X
74bdd1243dSDimitry Andric // encodes access size
75bdd1243dSDimitry Andric register uptr x10 asm("x10") = p;
76bdd1243dSDimitry Andric asm volatile(
77bdd1243dSDimitry Andric "ebreak\n"
78bdd1243dSDimitry Andric "addiw x0, x0, %1\n" ::"r"(x10),
79*06c3fb27SDimitry Andric "I"(0x40 + SigTrapEncoding(EA, AT, LogSize)));
800b57cec5SDimitry Andric #else
810b57cec5SDimitry Andric // FIXME: not always sigill.
820b57cec5SDimitry Andric __builtin_trap();
830b57cec5SDimitry Andric #endif
840b57cec5SDimitry Andric // __builtin_unreachable();
850b57cec5SDimitry Andric }
860b57cec5SDimitry Andric
870b57cec5SDimitry Andric // Version with access size which is not power of 2
88*06c3fb27SDimitry Andric template <ErrorAction EA, AccessType AT>
SigTrap(uptr p,uptr size)890b57cec5SDimitry Andric __attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
90*06c3fb27SDimitry Andric // Other platforms like linux can use signals for intercepting an exception
91*06c3fb27SDimitry Andric // and dispatching to HandleTagMismatch. The fuchsias implementation doesn't
92*06c3fb27SDimitry Andric // use signals so we can call it here directly instead.
93*06c3fb27SDimitry Andric #if CAN_GET_REGISTERS && SANITIZER_FUCHSIA
94*06c3fb27SDimitry Andric auto regs = GetRegisters();
95*06c3fb27SDimitry Andric AccessInfo access_info = {
96*06c3fb27SDimitry Andric .addr = p,
97*06c3fb27SDimitry Andric .size = size,
98*06c3fb27SDimitry Andric .is_store = AT == AccessType::Store,
99*06c3fb27SDimitry Andric .is_load = AT == AccessType::Load,
100*06c3fb27SDimitry Andric .recover = EA == ErrorAction::Recover,
101*06c3fb27SDimitry Andric };
102*06c3fb27SDimitry Andric HandleTagMismatch(access_info, (uptr)__builtin_return_address(0),
103*06c3fb27SDimitry Andric (uptr)__builtin_frame_address(0), /*uc=*/nullptr, regs.x);
104*06c3fb27SDimitry Andric #elif defined(__aarch64__)
1050b57cec5SDimitry Andric register uptr x0 asm("x0") = p;
1060b57cec5SDimitry Andric register uptr x1 asm("x1") = size;
107*06c3fb27SDimitry Andric asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + SigTrapEncoding(EA, AT)));
1080b57cec5SDimitry Andric #elif defined(__x86_64__)
1090b57cec5SDimitry Andric // Size is stored in rsi.
1100b57cec5SDimitry Andric asm volatile(
1110b57cec5SDimitry Andric "int3\n"
112*06c3fb27SDimitry Andric "nopl %c0(%%rax)\n" ::"n"(0x40 + SigTrapEncoding(EA, AT)),
1130b57cec5SDimitry Andric "D"(p), "S"(size));
114bdd1243dSDimitry Andric #elif SANITIZER_RISCV64
115bdd1243dSDimitry Andric // Put access size into x11
116bdd1243dSDimitry Andric register uptr x10 asm("x10") = p;
117bdd1243dSDimitry Andric register uptr x11 asm("x11") = size;
118bdd1243dSDimitry Andric asm volatile(
119bdd1243dSDimitry Andric "ebreak\n"
120bdd1243dSDimitry Andric "addiw x0, x0, %2\n" ::"r"(x10),
121*06c3fb27SDimitry Andric "r"(x11), "I"(0x40 + SigTrapEncoding(EA, AT)));
1220b57cec5SDimitry Andric #else
1230b57cec5SDimitry Andric __builtin_trap();
1240b57cec5SDimitry Andric #endif
1250b57cec5SDimitry Andric // __builtin_unreachable();
1260b57cec5SDimitry Andric }
1270b57cec5SDimitry Andric
ShortTagSize(tag_t mem_tag,uptr ptr)128*06c3fb27SDimitry Andric __attribute__((always_inline, nodebug)) static inline uptr ShortTagSize(
129*06c3fb27SDimitry Andric tag_t mem_tag, uptr ptr) {
130*06c3fb27SDimitry Andric DCHECK(IsAligned(ptr, kShadowAlignment));
131*06c3fb27SDimitry Andric tag_t ptr_tag = GetTagFromPointer(ptr);
132*06c3fb27SDimitry Andric if (ptr_tag == mem_tag)
133*06c3fb27SDimitry Andric return kShadowAlignment;
134*06c3fb27SDimitry Andric if (!mem_tag || mem_tag >= kShadowAlignment)
135*06c3fb27SDimitry Andric return 0;
136*06c3fb27SDimitry Andric if (*(u8 *)(ptr | (kShadowAlignment - 1)) != ptr_tag)
137*06c3fb27SDimitry Andric return 0;
138*06c3fb27SDimitry Andric return mem_tag;
139*06c3fb27SDimitry Andric }
140*06c3fb27SDimitry Andric
141*06c3fb27SDimitry Andric __attribute__((always_inline, nodebug)) static inline bool
PossiblyShortTagMatches(tag_t mem_tag,uptr ptr,uptr sz)142*06c3fb27SDimitry Andric PossiblyShortTagMatches(tag_t mem_tag, uptr ptr, uptr sz) {
1430b57cec5SDimitry Andric tag_t ptr_tag = GetTagFromPointer(ptr);
1440b57cec5SDimitry Andric if (ptr_tag == mem_tag)
1450b57cec5SDimitry Andric return true;
1460b57cec5SDimitry Andric if (mem_tag >= kShadowAlignment)
1470b57cec5SDimitry Andric return false;
1480b57cec5SDimitry Andric if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag)
1490b57cec5SDimitry Andric return false;
1500b57cec5SDimitry Andric return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag;
1510b57cec5SDimitry Andric }
1520b57cec5SDimitry Andric
1530b57cec5SDimitry Andric template <ErrorAction EA, AccessType AT, unsigned LogSize>
CheckAddress(uptr p)1540b57cec5SDimitry Andric __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
155fe6060f1SDimitry Andric if (!InTaggableRegion(p))
156fe6060f1SDimitry Andric return;
1570b57cec5SDimitry Andric uptr ptr_raw = p & ~kAddressTagMask;
1580b57cec5SDimitry Andric tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
1590b57cec5SDimitry Andric if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) {
160*06c3fb27SDimitry Andric SigTrap<EA, AT, LogSize>(p);
1610b57cec5SDimitry Andric if (EA == ErrorAction::Abort)
1620b57cec5SDimitry Andric __builtin_unreachable();
1630b57cec5SDimitry Andric }
1640b57cec5SDimitry Andric }
1650b57cec5SDimitry Andric
1660b57cec5SDimitry Andric template <ErrorAction EA, AccessType AT>
CheckAddressSized(uptr p,uptr sz)1670b57cec5SDimitry Andric __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
1680b57cec5SDimitry Andric uptr sz) {
169fe6060f1SDimitry Andric if (sz == 0 || !InTaggableRegion(p))
1700b57cec5SDimitry Andric return;
1710b57cec5SDimitry Andric tag_t ptr_tag = GetTagFromPointer(p);
1720b57cec5SDimitry Andric uptr ptr_raw = p & ~kAddressTagMask;
1730b57cec5SDimitry Andric tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
1740b57cec5SDimitry Andric tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz);
1750b57cec5SDimitry Andric for (tag_t *t = shadow_first; t < shadow_last; ++t)
1760b57cec5SDimitry Andric if (UNLIKELY(ptr_tag != *t)) {
177*06c3fb27SDimitry Andric SigTrap<EA, AT>(p, sz);
1780b57cec5SDimitry Andric if (EA == ErrorAction::Abort)
1790b57cec5SDimitry Andric __builtin_unreachable();
1800b57cec5SDimitry Andric }
1810b57cec5SDimitry Andric uptr end = p + sz;
182*06c3fb27SDimitry Andric uptr tail_sz = end & (kShadowAlignment - 1);
1830b57cec5SDimitry Andric if (UNLIKELY(tail_sz != 0 &&
1840b57cec5SDimitry Andric !PossiblyShortTagMatches(
1850b57cec5SDimitry Andric *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) {
186*06c3fb27SDimitry Andric SigTrap<EA, AT>(p, sz);
1870b57cec5SDimitry Andric if (EA == ErrorAction::Abort)
1880b57cec5SDimitry Andric __builtin_unreachable();
1890b57cec5SDimitry Andric }
1900b57cec5SDimitry Andric }
1910b57cec5SDimitry Andric
1920b57cec5SDimitry Andric } // end namespace __hwasan
1930b57cec5SDimitry Andric
1940b57cec5SDimitry Andric #endif // HWASAN_CHECKS_H
195