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
50INTERCEPTOR(int, pthread_join, void *t, void **arg) {
51  return REAL(pthread_join)(t, arg);
52}
53
54DEFINE_REAL_PTHREAD_FUNCTIONS
55
56DEFINE_REAL(int, vfork)
57DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
58
59// Get and/or change the set of blocked signals.
60extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
61                           __hw_sigset_t *__restrict __oset);
62#define SIG_BLOCK 0
63#define SIG_SETMASK 2
64extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
65  env[0].__magic = kHwJmpBufMagic;
66  env[0].__mask_was_saved =
67      (savemask && sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0,
68                               &env[0].__saved_mask) == 0);
69  return 0;
70}
71
72static void __attribute__((always_inline))
73InternalLongjmp(__hw_register_buf env, int retval) {
74#    if defined(__aarch64__)
75  constexpr size_t kSpIndex = 13;
76#    elif defined(__x86_64__)
77  constexpr size_t kSpIndex = 6;
78#    elif SANITIZER_RISCV64
79  constexpr size_t kSpIndex = 13;
80#    endif
81
82  // Clear all memory tags on the stack between here and where we're going.
83  unsigned long long stack_pointer = env[kSpIndex];
84  // The stack pointer should never be tagged, so we don't need to clear the
85  // tag for this function call.
86  __hwasan_handle_longjmp((void *)stack_pointer);
87
88  // Run code for handling a longjmp.
89  // Need to use a register that isn't going to be loaded from the environment
90  // buffer -- hence why we need to specify the register to use.
91  // Must implement this ourselves, since we don't know the order of registers
92  // in different libc implementations and many implementations mangle the
93  // stack pointer so we can't use it without knowing the demangling scheme.
94#    if defined(__aarch64__)
95  register long int retval_tmp asm("x1") = retval;
96  register void *env_address asm("x0") = &env[0];
97  asm volatile("ldp	x19, x20, [%0, #0<<3];"
98               "ldp	x21, x22, [%0, #2<<3];"
99               "ldp	x23, x24, [%0, #4<<3];"
100               "ldp	x25, x26, [%0, #6<<3];"
101               "ldp	x27, x28, [%0, #8<<3];"
102               "ldp	x29, x30, [%0, #10<<3];"
103               "ldp	 d8,  d9, [%0, #14<<3];"
104               "ldp	d10, d11, [%0, #16<<3];"
105               "ldp	d12, d13, [%0, #18<<3];"
106               "ldp	d14, d15, [%0, #20<<3];"
107               "ldr	x5, [%0, #13<<3];"
108               "mov	sp, x5;"
109               // Return the value requested to return through arguments.
110               // This should be in x1 given what we requested above.
111               "cmp	%1, #0;"
112               "mov	x0, #1;"
113               "csel	x0, %1, x0, ne;"
114               "br	x30;"
115               : "+r"(env_address)
116               : "r"(retval_tmp));
117#    elif defined(__x86_64__)
118  register long int retval_tmp asm("%rsi") = retval;
119  register void *env_address asm("%rdi") = &env[0];
120  asm volatile(
121      // Restore registers.
122      "mov (0*8)(%0),%%rbx;"
123      "mov (1*8)(%0),%%rbp;"
124      "mov (2*8)(%0),%%r12;"
125      "mov (3*8)(%0),%%r13;"
126      "mov (4*8)(%0),%%r14;"
127      "mov (5*8)(%0),%%r15;"
128      "mov (6*8)(%0),%%rsp;"
129      "mov (7*8)(%0),%%rdx;"
130      // Return 1 if retval is 0.
131      "mov $1,%%rax;"
132      "test %1,%1;"
133      "cmovnz %1,%%rax;"
134      "jmp *%%rdx;" ::"r"(env_address),
135      "r"(retval_tmp));
136#    elif SANITIZER_RISCV64
137  register long int retval_tmp asm("x11") = retval;
138  register void *env_address asm("x10") = &env[0];
139  asm volatile(
140      "ld     ra,   0<<3(%0);"
141      "ld     s0,   1<<3(%0);"
142      "ld     s1,   2<<3(%0);"
143      "ld     s2,   3<<3(%0);"
144      "ld     s3,   4<<3(%0);"
145      "ld     s4,   5<<3(%0);"
146      "ld     s5,   6<<3(%0);"
147      "ld     s6,   7<<3(%0);"
148      "ld     s7,   8<<3(%0);"
149      "ld     s8,   9<<3(%0);"
150      "ld     s9,   10<<3(%0);"
151      "ld     s10,  11<<3(%0);"
152      "ld     s11,  12<<3(%0);"
153#      if __riscv_float_abi_double
154      "fld    fs0,  14<<3(%0);"
155      "fld    fs1,  15<<3(%0);"
156      "fld    fs2,  16<<3(%0);"
157      "fld    fs3,  17<<3(%0);"
158      "fld    fs4,  18<<3(%0);"
159      "fld    fs5,  19<<3(%0);"
160      "fld    fs6,  20<<3(%0);"
161      "fld    fs7,  21<<3(%0);"
162      "fld    fs8,  22<<3(%0);"
163      "fld    fs9,  23<<3(%0);"
164      "fld    fs10, 24<<3(%0);"
165      "fld    fs11, 25<<3(%0);"
166#      elif __riscv_float_abi_soft
167#      else
168#        error "Unsupported case"
169#      endif
170      "ld     a4, 13<<3(%0);"
171      "mv     sp, a4;"
172      // Return the value requested to return through arguments.
173      // This should be in x11 given what we requested above.
174      "seqz   a0, %1;"
175      "add    a0, a0, %1;"
176      "ret;"
177      : "+r"(env_address)
178      : "r"(retval_tmp));
179#    endif
180}
181
182INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
183  if (env[0].__magic != kHwJmpBufMagic) {
184    Printf(
185        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
186        "there is a bug in HWASan.\n");
187    return REAL(siglongjmp)(env, val);
188  }
189
190  if (env[0].__mask_was_saved)
191    // Restore the saved signal mask.
192    (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask,
193                      (__hw_sigset_t *)0);
194  InternalLongjmp(env[0].__jmpbuf, val);
195}
196
197// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
198// _setjmp on start_thread.  Hence we have to intercept the longjmp on
199// pthread_exit so the __hw_jmp_buf order matches.
200INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
201  if (env[0].__magic != kHwJmpBufMagic)
202    return REAL(__libc_longjmp)(env, val);
203  InternalLongjmp(env[0].__jmpbuf, val);
204}
205
206INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
207  if (env[0].__magic != kHwJmpBufMagic) {
208    Printf(
209        "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
210        "there is a bug in HWASan.\n");
211    return REAL(longjmp)(env, val);
212  }
213  InternalLongjmp(env[0].__jmpbuf, val);
214}
215#undef SIG_BLOCK
216#undef SIG_SETMASK
217
218#  endif  // HWASAN_WITH_INTERCEPTORS
219
220namespace __hwasan {
221
222int OnExit() {
223  if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks &&
224      __lsan::HasReportedLeaks()) {
225    return common_flags()->exitcode;
226  }
227  // FIXME: ask frontend whether we need to return failure.
228  return 0;
229}
230
231} // namespace __hwasan
232
233namespace __hwasan {
234
235void InitializeInterceptors() {
236  static int inited = 0;
237  CHECK_EQ(inited, 0);
238
239#if HWASAN_WITH_INTERCEPTORS
240#if defined(__linux__)
241  INTERCEPT_FUNCTION(__libc_longjmp);
242  INTERCEPT_FUNCTION(longjmp);
243  INTERCEPT_FUNCTION(siglongjmp);
244  INTERCEPT_FUNCTION(vfork);
245#endif  // __linux__
246  INTERCEPT_FUNCTION(pthread_create);
247  INTERCEPT_FUNCTION(pthread_join);
248#  endif
249
250  inited = 1;
251}
252} // namespace __hwasan
253
254#endif  // #if !SANITIZER_FUCHSIA
255