1343175Sdim//===-- hwasan_checks.h -----------------------------------------*- C++ -*-===//
2343175Sdim//
3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353358Sdim// See https://llvm.org/LICENSE.txt for license information.
5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6343175Sdim//
7343175Sdim//===----------------------------------------------------------------------===//
8343175Sdim//
9343175Sdim// This file is a part of HWAddressSanitizer.
10343175Sdim//
11343175Sdim//===----------------------------------------------------------------------===//
12343175Sdim
13343175Sdim#ifndef HWASAN_CHECKS_H
14343175Sdim#define HWASAN_CHECKS_H
15343175Sdim
16343175Sdim#include "hwasan_mapping.h"
17353358Sdim#include "sanitizer_common/sanitizer_common.h"
18343175Sdim
19343175Sdimnamespace __hwasan {
20343175Sdimtemplate <unsigned X>
21343175Sdim__attribute__((always_inline)) static void SigTrap(uptr p) {
22343175Sdim#if defined(__aarch64__)
23343175Sdim  (void)p;
24343175Sdim  // 0x900 is added to do not interfere with the kernel use of lower values of
25343175Sdim  // brk immediate.
26353358Sdim  register uptr x0 asm("x0") = p;
27353358Sdim  asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + X));
28343175Sdim#elif defined(__x86_64__)
29343175Sdim  // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
30343175Sdim  // total. The pointer is passed via rdi.
31343175Sdim  // 0x40 is added as a safeguard, to help distinguish our trap from others and
32343175Sdim  // to avoid 0 offsets in the command (otherwise it'll be reduced to a
33343175Sdim  // different nop command, the three bytes one).
34343175Sdim  asm volatile(
35343175Sdim      "int3\n"
36343175Sdim      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
37343175Sdim      "D"(p));
38343175Sdim#else
39343175Sdim  // FIXME: not always sigill.
40343175Sdim  __builtin_trap();
41343175Sdim#endif
42343175Sdim  // __builtin_unreachable();
43343175Sdim}
44343175Sdim
45353358Sdim// Version with access size which is not power of 2
46353358Sdimtemplate <unsigned X>
47353358Sdim__attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
48353358Sdim#if defined(__aarch64__)
49353358Sdim  register uptr x0 asm("x0") = p;
50353358Sdim  register uptr x1 asm("x1") = size;
51353358Sdim  asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + X));
52353358Sdim#elif defined(__x86_64__)
53353358Sdim  // Size is stored in rsi.
54353358Sdim  asm volatile(
55353358Sdim      "int3\n"
56353358Sdim      "nopl %c0(%%rax)\n" ::"n"(0x40 + X),
57353358Sdim      "D"(p), "S"(size));
58353358Sdim#else
59353358Sdim  __builtin_trap();
60353358Sdim#endif
61353358Sdim  // __builtin_unreachable();
62353358Sdim}
63353358Sdim
64353358Sdim__attribute__((always_inline, nodebug)) static bool PossiblyShortTagMatches(
65353358Sdim    tag_t mem_tag, uptr ptr, uptr sz) {
66353358Sdim  tag_t ptr_tag = GetTagFromPointer(ptr);
67353358Sdim  if (ptr_tag == mem_tag)
68353358Sdim    return true;
69353358Sdim  if (mem_tag >= kShadowAlignment)
70353358Sdim    return false;
71353358Sdim  if ((ptr & (kShadowAlignment - 1)) + sz > mem_tag)
72353358Sdim    return false;
73353358Sdim#ifndef __aarch64__
74353358Sdim  ptr = UntagAddr(ptr);
75353358Sdim#endif
76353358Sdim  return *(u8 *)(ptr | (kShadowAlignment - 1)) == ptr_tag;
77353358Sdim}
78353358Sdim
79343175Sdimenum class ErrorAction { Abort, Recover };
80343175Sdimenum class AccessType { Load, Store };
81343175Sdim
82343175Sdimtemplate <ErrorAction EA, AccessType AT, unsigned LogSize>
83343175Sdim__attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) {
84343175Sdim  uptr ptr_raw = p & ~kAddressTagMask;
85343175Sdim  tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw);
86353358Sdim  if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) {
87343175Sdim    SigTrap<0x20 * (EA == ErrorAction::Recover) +
88343175Sdim            0x10 * (AT == AccessType::Store) + LogSize>(p);
89343175Sdim    if (EA == ErrorAction::Abort)
90343175Sdim      __builtin_unreachable();
91343175Sdim  }
92343175Sdim}
93343175Sdim
94343175Sdimtemplate <ErrorAction EA, AccessType AT>
95343175Sdim__attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
96343175Sdim                                                                      uptr sz) {
97343175Sdim  if (sz == 0)
98343175Sdim    return;
99343175Sdim  tag_t ptr_tag = GetTagFromPointer(p);
100343175Sdim  uptr ptr_raw = p & ~kAddressTagMask;
101343175Sdim  tag_t *shadow_first = (tag_t *)MemToShadow(ptr_raw);
102353358Sdim  tag_t *shadow_last = (tag_t *)MemToShadow(ptr_raw + sz);
103353358Sdim  for (tag_t *t = shadow_first; t < shadow_last; ++t)
104343175Sdim    if (UNLIKELY(ptr_tag != *t)) {
105343175Sdim      SigTrap<0x20 * (EA == ErrorAction::Recover) +
106353358Sdim              0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
107343175Sdim      if (EA == ErrorAction::Abort)
108343175Sdim        __builtin_unreachable();
109343175Sdim    }
110353358Sdim  uptr end = p + sz;
111353358Sdim  uptr tail_sz = end & 0xf;
112353358Sdim  if (UNLIKELY(tail_sz != 0 &&
113353358Sdim               !PossiblyShortTagMatches(
114353358Sdim                   *shadow_last, end & ~(kShadowAlignment - 1), tail_sz))) {
115353358Sdim    SigTrap<0x20 * (EA == ErrorAction::Recover) +
116353358Sdim            0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
117353358Sdim    if (EA == ErrorAction::Abort)
118353358Sdim      __builtin_unreachable();
119353358Sdim  }
120343175Sdim}
121353358Sdim
122343175Sdim}  // end namespace __hwasan
123343175Sdim
124343175Sdim#endif  // HWASAN_CHECKS_H
125