1//===-- hwasan_interceptors.cpp -------------------------------------------===//
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// Interceptors for standard library functions.
12//
13// FIXME: move as many interceptors as possible into
14// sanitizer_common/sanitizer_common_interceptors.h
15//===----------------------------------------------------------------------===//
16
17#include "interception/interception.h"
18#include "hwasan.h"
19#include "hwasan_thread.h"
20#include "sanitizer_common/sanitizer_stackdepot.h"
21
22#if !SANITIZER_FUCHSIA
23
24using namespace __hwasan;
25
26#if HWASAN_WITH_INTERCEPTORS
27
28struct ThreadStartArg {
29  thread_callback_t callback;
30  void *param;
31};
32
33static void *HwasanThreadStartFunc(void *arg) {
34  __hwasan_thread_enter();
35  ThreadStartArg A = *reinterpret_cast<ThreadStartArg*>(arg);
36  UnmapOrDie(arg, GetPageSizeCached());
37  return A.callback(A.param);
38}
39
40INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*),
41            void * param) {
42  ScopedTaggingDisabler disabler;
43  ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie(
44      GetPageSizeCached(), "pthread_create"));
45  *A = {callback, param};
46  int res = REAL(pthread_create)(th, attr, &HwasanThreadStartFunc, A);
47  return res;
48}
49
50DEFINE_REAL(int, vfork)
51DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
52
53// Get and/or change the set of blocked signals.
54extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
55                           __hw_sigset_t *__restrict __oset);
56#define SIG_BLOCK 0
57#define SIG_SETMASK 2
58extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
59  env[0].__magic = kHwJmpBufMagic;
60  env[0].__mask_was_saved =
61      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
62                               &env[0].__saved_mask) == 0);
63  return 0;
64}
65
66static void __attribute__((always_inline))
67InternalLongjmp(__hw_register_buf env, int retval) {
68#    if defined(__aarch64__)
69  constexpr size_t kSpIndex = 13;
70#    elif defined(__x86_64__)
71  constexpr size_t kSpIndex = 6;
72#    endif
73
74  // Clear all memory tags on the stack between here and where we're going.
75  unsigned long long stack_pointer = env[kSpIndex];
76  // The stack pointer should never be tagged, so we don't need to clear the
77  // tag for this function call.
78  __hwasan_handle_longjmp((void *)stack_pointer);
79
80  // Run code for handling a longjmp.
81  // Need to use a register that isn't going to be loaded from the environment
82  // buffer -- hence why we need to specify the register to use.
83  // Must implement this ourselves, since we don't know the order of registers
84  // in different libc implementations and many implementations mangle the
85  // stack pointer so we can't use it without knowing the demangling scheme.
86#    if defined(__aarch64__)
87  register long int retval_tmp asm("x1") = retval;
88  register void *env_address asm("x0") = &env[0];
89  asm volatile("ldp	x19, x20, [%0, #0<<3];"
90               "ldp	x21, x22, [%0, #2<<3];"
91               "ldp	x23, x24, [%0, #4<<3];"
92               "ldp	x25, x26, [%0, #6<<3];"
93               "ldp	x27, x28, [%0, #8<<3];"
94               "ldp	x29, x30, [%0, #10<<3];"
95               "ldp	 d8,  d9, [%0, #14<<3];"
96               "ldp	d10, d11, [%0, #16<<3];"
97               "ldp	d12, d13, [%0, #18<<3];"
98               "ldp	d14, d15, [%0, #20<<3];"
99               "ldr	x5, [%0, #13<<3];"
100               "mov	sp, x5;"
101               // Return the value requested to return through arguments.
102               // This should be in x1 given what we requested above.
103               "cmp	%1, #0;"
104               "mov	x0, #1;"
105               "csel	x0, %1, x0, ne;"
106               "br	x30;"
107               : "+r"(env_address)
108               : "r"(retval_tmp));
109#    elif defined(__x86_64__)
110  register long int retval_tmp asm("%rsi") = retval;
111  register void *env_address asm("%rdi") = &env[0];
112  asm volatile(
113      // Restore registers.
114      "mov (0*8)(%0),%%rbx;"
115      "mov (1*8)(%0),%%rbp;"
116      "mov (2*8)(%0),%%r12;"
117      "mov (3*8)(%0),%%r13;"
118      "mov (4*8)(%0),%%r14;"
119      "mov (5*8)(%0),%%r15;"
120      "mov (6*8)(%0),%%rsp;"
121      "mov (7*8)(%0),%%rdx;"
122      // Return 1 if retval is 0.
123      "mov $1,%%rax;"
124      "test %1,%1;"
125      "cmovnz %1,%%rax;"
126      "jmp *%%rdx;" ::"r"(env_address),
127      "r"(retval_tmp));
128#    endif
129}
130
131INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
132  if (env[0].__magic != kHwJmpBufMagic) {
133    Printf(
134        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
135        "there is a bug in HWASan.\n");
136    return REAL(siglongjmp)(env, val);
137  }
138
139  if (env[0].__mask_was_saved)
140    // Restore the saved signal mask.
141    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
142                      (__hw_sigset_t *)0);
143  InternalLongjmp(env[0].__jmpbuf, val);
144}
145
146// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
147// _setjmp on start_thread.  Hence we have to intercept the longjmp on
148// pthread_exit so the __hw_jmp_buf order matches.
149INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
150  if (env[0].__magic != kHwJmpBufMagic)
151    return REAL(__libc_longjmp)(env, val);
152  InternalLongjmp(env[0].__jmpbuf, val);
153}
154
155INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
156  if (env[0].__magic != kHwJmpBufMagic) {
157    Printf(
158        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
159        "there is a bug in HWASan.\n");
160    return REAL(longjmp)(env, val);
161  }
162  InternalLongjmp(env[0].__jmpbuf, val);
163}
164#undef SIG_BLOCK
165#undef SIG_SETMASK
166
167#  endif  // HWASAN_WITH_INTERCEPTORS
168
169namespace __hwasan {
170
171int OnExit() {
172  // FIXME: ask frontend whether we need to return failure.
173  return 0;
174}
175
176} // namespace __hwasan
177
178namespace __hwasan {
179
180void InitializeInterceptors() {
181  static int inited = 0;
182  CHECK_EQ(inited, 0);
183
184#if HWASAN_WITH_INTERCEPTORS
185#if defined(__linux__)
186  INTERCEPT_FUNCTION(__libc_longjmp);
187  INTERCEPT_FUNCTION(longjmp);
188  INTERCEPT_FUNCTION(siglongjmp);
189  INTERCEPT_FUNCTION(vfork);
190#endif  // __linux__
191  INTERCEPT_FUNCTION(pthread_create);
192#endif
193
194  inited = 1;
195}
196} // namespace __hwasan
197
198#endif  // #if !SANITIZER_FUCHSIA
199