xref: /freebsd/contrib/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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 #define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
18 
19 #include "hwasan.h"
20 #include "hwasan_allocator.h"
21 #include "hwasan_checks.h"
22 #include "hwasan_mapping.h"
23 #include "hwasan_platform_interceptors.h"
24 #include "hwasan_thread.h"
25 #include "hwasan_thread_list.h"
26 #include "interception/interception.h"
27 #include "sanitizer_common/sanitizer_errno.h"
28 #include "sanitizer_common/sanitizer_linux.h"
29 #include "sanitizer_common/sanitizer_stackdepot.h"
30 
31 #if !SANITIZER_FUCHSIA
32 
33 using namespace __hwasan;
34 
35 struct HWAsanInterceptorContext {
36   const char *interceptor_name;
37 };
38 
39 #  define ACCESS_MEMORY_RANGE(offset, size, access)                           \
40     do {                                                                      \
41       __hwasan::CheckAddressSized<ErrorAction::Recover, access>((uptr)offset, \
42                                                                 size);        \
43     } while (0)
44 
45 #  define HWASAN_READ_RANGE(offset, size) \
46     ACCESS_MEMORY_RANGE(offset, size, AccessType::Load)
47 #  define HWASAN_WRITE_RANGE(offset, size) \
48     ACCESS_MEMORY_RANGE(offset, size, AccessType::Store)
49 
50 #  if !SANITIZER_APPLE
51 #    define HWASAN_INTERCEPT_FUNC(name)                                        \
52       do {                                                                     \
53         if (!INTERCEPT_FUNCTION(name))                                         \
54           VReport(1, "HWAddressSanitizer: failed to intercept '%s'\n", #name); \
55       } while (0)
56 #    define HWASAN_INTERCEPT_FUNC_VER(name, ver)                           \
57       do {                                                                 \
58         if (!INTERCEPT_FUNCTION_VER(name, ver))                            \
59           VReport(1, "HWAddressSanitizer: failed to intercept '%s@@%s'\n", \
60                   #name, ver);                                             \
61       } while (0)
62 #    define HWASAN_INTERCEPT_FUNC_VER_UNVERSIONED_FALLBACK(name, ver)          \
63       do {                                                                     \
64         if (!INTERCEPT_FUNCTION_VER(name, ver) && !INTERCEPT_FUNCTION(name))   \
65           VReport(                                                             \
66               1, "HWAddressSanitizer: failed to intercept '%s@@%s' or '%s'\n", \
67               #name, ver, #name);                                              \
68       } while (0)
69 
70 #  else
71 // OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
72 #    define HWASAN_INTERCEPT_FUNC(name)
73 #  endif  // SANITIZER_APPLE
74 
75 #  if HWASAN_WITH_INTERCEPTORS
76 
77 #    define COMMON_SYSCALL_PRE_READ_RANGE(p, s) HWASAN_READ_RANGE(p, s)
78 #    define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) HWASAN_WRITE_RANGE(p, s)
79 #    define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
80       do {                                       \
81         (void)(p);                               \
82         (void)(s);                               \
83       } while (false)
84 #    define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
85       do {                                        \
86         (void)(p);                                \
87         (void)(s);                                \
88       } while (false)
89 #    include "sanitizer_common/sanitizer_common_syscalls.inc"
90 #    include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
91 
92 #    define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
93       HWASAN_WRITE_RANGE(ptr, size)
94 
95 #    define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
96       HWASAN_READ_RANGE(ptr, size)
97 
98 #    define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
99       HWAsanInterceptorContext _ctx = {#func};       \
100       ctx = (void *)&_ctx;                           \
101       do {                                           \
102         (void)(ctx);                                 \
103         (void)(func);                                \
104       } while (false)
105 
106 #    define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
107       do {                                            \
108         (void)(ctx);                                  \
109         (void)(path);                                 \
110       } while (false)
111 
112 #    define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
113       do {                                         \
114         (void)(ctx);                               \
115         (void)(fd);                                \
116       } while (false)
117 
118 #    define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
119       do {                                         \
120         (void)(ctx);                               \
121         (void)(fd);                                \
122       } while (false)
123 
124 #    define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
125       do {                                                      \
126         (void)(ctx);                                            \
127         (void)(fd);                                             \
128         (void)(newfd);                                          \
129       } while (false)
130 
131 #    define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
132       do {                                                \
133         (void)(ctx);                                      \
134         (void)(name);                                     \
135       } while (false)
136 
137 #    define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
138       do {                                                         \
139         (void)(ctx);                                               \
140         (void)(thread);                                            \
141         (void)(name);                                              \
142       } while (false)
143 
144 #    define COMMON_INTERCEPTOR_BLOCK_REAL(name) \
145       do {                                      \
146         (void)(name);                           \
147       } while (false)
148 
149 #    define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size)   \
150       {                                                         \
151         if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED)          \
152           return internal_memset(dst, v, size);                 \
153         COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size);    \
154         if (MemIsApp(UntagAddr(reinterpret_cast<uptr>(dst))) && \
155             common_flags()->intercept_intrin)                   \
156           COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size);       \
157         return REAL(memset)(dst, v, size);                      \
158       }
159 
160 #    define COMMON_INTERCEPTOR_STRERROR() \
161       do {                                \
162       } while (false)
163 
164 #    define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
165 
166 #    define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!hwasan_inited)
167 
168 // The main purpose of the mmap interceptor is to prevent the user from
169 // allocating on top of shadow pages.
170 //
171 // For compatibility, it does not tag pointers, nor does it allow
172 // MAP_FIXED in combination with a tagged pointer. (Since mmap itself
173 // will not return a tagged pointer, the tagged pointer must have come
174 // from elsewhere, such as the secondary allocator, which makes it a
175 // very odd usecase.)
176 template <class Mmap>
177 static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length,
178                               int prot, int flags, int fd, OFF64_T offset) {
179   if (addr) {
180     if (flags & map_fixed) CHECK_EQ(addr, UntagPtr(addr));
181 
182     addr = UntagPtr(addr);
183   }
184   SIZE_T rounded_length = RoundUpTo(length, GetPageSize());
185   void *end_addr = (char *)addr + (rounded_length - 1);
186   if (addr && length &&
187       (!MemIsApp(reinterpret_cast<uptr>(addr)) ||
188        !MemIsApp(reinterpret_cast<uptr>(end_addr)))) {
189     // User requested an address that is incompatible with HWASan's
190     // memory layout. Use a different address if allowed, else fail.
191     if (flags & map_fixed) {
192       errno = errno_EINVAL;
193       return (void *)-1;
194     } else {
195       addr = nullptr;
196     }
197   }
198   void *res = real_mmap(addr, length, prot, flags, fd, offset);
199   if (length && res != (void *)-1) {
200     uptr beg = reinterpret_cast<uptr>(res);
201     DCHECK(IsAligned(beg, GetPageSize()));
202     if (!MemIsApp(beg) || !MemIsApp(beg + rounded_length - 1)) {
203       // Application has attempted to map more memory than is supported by
204       // HWASan. Act as if we ran out of memory.
205       internal_munmap(res, length);
206       errno = errno_ENOMEM;
207       return (void *)-1;
208     }
209     __hwasan::TagMemoryAligned(beg, rounded_length, 0);
210   }
211 
212   return res;
213 }
214 
215 template <class Munmap>
216 static int munmap_interceptor(Munmap real_munmap, void *addr, SIZE_T length) {
217   // We should not tag if munmap fail, but it's to late to tag after
218   // real_munmap, as the pages could be mmaped by another thread.
219   uptr beg = reinterpret_cast<uptr>(addr);
220   if (length && IsAligned(beg, GetPageSize())) {
221     SIZE_T rounded_length = RoundUpTo(length, GetPageSize());
222     // Protect from unmapping the shadow.
223     if (!MemIsApp(beg) || !MemIsApp(beg + rounded_length - 1)) {
224       errno = errno_EINVAL;
225       return -1;
226     }
227     __hwasan::TagMemoryAligned(beg, rounded_length, 0);
228   }
229   return real_munmap(addr, length);
230 }
231 
232 #    define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, \
233                                          fd, offset)                           \
234       do {                                                                     \
235         (void)(ctx);                                                           \
236         return mmap_interceptor(REAL(mmap), addr, sz, prot, flags, fd, off);   \
237       } while (false)
238 
239 #    define COMMON_INTERCEPTOR_MUNMAP_IMPL(ctx, addr, length)          \
240       do {                                                             \
241         (void)(ctx);                                                   \
242         return munmap_interceptor(REAL(munmap), addr, sz);             \
243       } while (false)
244 
245 #    include "sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc"
246 #    include "sanitizer_common/sanitizer_common_interceptors.inc"
247 
248 struct ThreadStartArg {
249   __sanitizer_sigset_t starting_sigset_;
250 };
251 
252 static void *HwasanThreadStartFunc(void *arg) {
253   __hwasan_thread_enter();
254   SetSigProcMask(&reinterpret_cast<ThreadStartArg *>(arg)->starting_sigset_,
255                  nullptr);
256   InternalFree(arg);
257   auto self = GetThreadSelf();
258   auto args = hwasanThreadArgRetval().GetArgs(self);
259   void *retval = (*args.routine)(args.arg_retval);
260   hwasanThreadArgRetval().Finish(self, retval);
261   return retval;
262 }
263 
264 extern "C" {
265 int pthread_attr_getdetachstate(void *attr, int *v);
266 }
267 
268 INTERCEPTOR(int, pthread_create, void *thread, void *attr,
269             void *(*callback)(void *), void *param) {
270   EnsureMainThreadIDIsCorrect();
271   ScopedTaggingDisabler tagging_disabler;
272   bool detached = [attr]() {
273     int d = 0;
274     return attr && !pthread_attr_getdetachstate(attr, &d) && IsStateDetached(d);
275   }();
276   ThreadStartArg *A = (ThreadStartArg *)InternalAlloc(sizeof(ThreadStartArg));
277   ScopedBlockSignals block(&A->starting_sigset_);
278   // ASAN uses the same approach to disable leaks from pthread_create.
279 #    if CAN_SANITIZE_LEAKS
280   __lsan::ScopedInterceptorDisabler lsan_disabler;
281 #    endif
282 
283   int result;
284   hwasanThreadArgRetval().Create(detached, {callback, param}, [&]() -> uptr {
285     result = REAL(pthread_create)(thread, attr, &HwasanThreadStartFunc, A);
286     return result ? 0 : *(uptr *)(thread);
287   });
288   if (result != 0)
289     InternalFree(A);
290   return result;
291 }
292 
293 INTERCEPTOR(int, pthread_join, void *thread, void **retval) {
294   int result;
295   hwasanThreadArgRetval().Join((uptr)thread, [&]() {
296     result = REAL(pthread_join)(thread, retval);
297     return !result;
298   });
299   return result;
300 }
301 
302 INTERCEPTOR(int, pthread_detach, void *thread) {
303   int result;
304   hwasanThreadArgRetval().Detach((uptr)thread, [&]() {
305     result = REAL(pthread_detach)(thread);
306     return !result;
307   });
308   return result;
309 }
310 
311 INTERCEPTOR(void, pthread_exit, void *retval) {
312   hwasanThreadArgRetval().Finish(GetThreadSelf(), retval);
313   REAL(pthread_exit)(retval);
314 }
315 
316 #    if SANITIZER_GLIBC
317 INTERCEPTOR(int, pthread_tryjoin_np, void *thread, void **ret) {
318   int result;
319   hwasanThreadArgRetval().Join((uptr)thread, [&]() {
320     result = REAL(pthread_tryjoin_np)(thread, ret);
321     return !result;
322   });
323   return result;
324 }
325 
326 INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,
327             const struct timespec *abstime) {
328   int result;
329   hwasanThreadArgRetval().Join((uptr)thread, [&]() {
330     result = REAL(pthread_timedjoin_np)(thread, ret, abstime);
331     return !result;
332   });
333   return result;
334 }
335 #    endif
336 
337 DEFINE_REAL_PTHREAD_FUNCTIONS
338 
339 DEFINE_REAL(int, vfork)
340 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork)
341 
342 // Get and/or change the set of blocked signals.
343 extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,
344                            __hw_sigset_t *__restrict __oset);
345 #    define SIG_BLOCK 0
346 #    define SIG_SETMASK 2
347 extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {
348   env[0].__magic = kHwJmpBufMagic;
349   env[0].__mask_was_saved =
350       (savemask &&
351        sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0, &env[0].__saved_mask) == 0);
352   return 0;
353 }
354 
355 static void __attribute__((always_inline))
356 InternalLongjmp(__hw_register_buf env, int retval) {
357 #    if defined(__aarch64__)
358   constexpr size_t kSpIndex = 13;
359 #    elif defined(__x86_64__)
360   constexpr size_t kSpIndex = 6;
361 #    elif SANITIZER_RISCV64
362   constexpr size_t kSpIndex = 13;
363 #    endif
364 
365   // Clear all memory tags on the stack between here and where we're going.
366   unsigned long long stack_pointer = env[kSpIndex];
367   // The stack pointer should never be tagged, so we don't need to clear the
368   // tag for this function call.
369   __hwasan_handle_longjmp((void *)stack_pointer);
370 
371   // Run code for handling a longjmp.
372   // Need to use a register that isn't going to be loaded from the environment
373   // buffer -- hence why we need to specify the register to use.
374   // Must implement this ourselves, since we don't know the order of registers
375   // in different libc implementations and many implementations mangle the
376   // stack pointer so we can't use it without knowing the demangling scheme.
377 #    if defined(__aarch64__)
378   register long int retval_tmp asm("x1") = retval;
379   register void *env_address asm("x0") = &env[0];
380   asm volatile(
381       "ldp	x19, x20, [%0, #0<<3];"
382       "ldp	x21, x22, [%0, #2<<3];"
383       "ldp	x23, x24, [%0, #4<<3];"
384       "ldp	x25, x26, [%0, #6<<3];"
385       "ldp	x27, x28, [%0, #8<<3];"
386       "ldp	x29, x30, [%0, #10<<3];"
387       "ldp	 d8,  d9, [%0, #14<<3];"
388       "ldp	d10, d11, [%0, #16<<3];"
389       "ldp	d12, d13, [%0, #18<<3];"
390       "ldp	d14, d15, [%0, #20<<3];"
391       "ldr	x5, [%0, #13<<3];"
392       "mov	sp, x5;"
393       // Return the value requested to return through arguments.
394       // This should be in x1 given what we requested above.
395       "cmp	%1, #0;"
396       "mov	x0, #1;"
397       "csel	x0, %1, x0, ne;"
398       "br	x30;"
399       : "+r"(env_address)
400       : "r"(retval_tmp));
401 #    elif defined(__x86_64__)
402   register long int retval_tmp asm("%rsi") = retval;
403   register void *env_address asm("%rdi") = &env[0];
404   asm volatile(
405       // Restore registers.
406       "mov (0*8)(%0),%%rbx;"
407       "mov (1*8)(%0),%%rbp;"
408       "mov (2*8)(%0),%%r12;"
409       "mov (3*8)(%0),%%r13;"
410       "mov (4*8)(%0),%%r14;"
411       "mov (5*8)(%0),%%r15;"
412       "mov (6*8)(%0),%%rsp;"
413       "mov (7*8)(%0),%%rdx;"
414       // Return 1 if retval is 0.
415       "mov $1,%%rax;"
416       "test %1,%1;"
417       "cmovnz %1,%%rax;"
418       "jmp *%%rdx;" ::"r"(env_address),
419       "r"(retval_tmp));
420 #    elif SANITIZER_RISCV64
421   register long int retval_tmp asm("x11") = retval;
422   register void *env_address asm("x10") = &env[0];
423   asm volatile(
424       "ld     ra,   0<<3(%0);"
425       "ld     s0,   1<<3(%0);"
426       "ld     s1,   2<<3(%0);"
427       "ld     s2,   3<<3(%0);"
428       "ld     s3,   4<<3(%0);"
429       "ld     s4,   5<<3(%0);"
430       "ld     s5,   6<<3(%0);"
431       "ld     s6,   7<<3(%0);"
432       "ld     s7,   8<<3(%0);"
433       "ld     s8,   9<<3(%0);"
434       "ld     s9,   10<<3(%0);"
435       "ld     s10,  11<<3(%0);"
436       "ld     s11,  12<<3(%0);"
437 #      if __riscv_float_abi_double
438       "fld    fs0,  14<<3(%0);"
439       "fld    fs1,  15<<3(%0);"
440       "fld    fs2,  16<<3(%0);"
441       "fld    fs3,  17<<3(%0);"
442       "fld    fs4,  18<<3(%0);"
443       "fld    fs5,  19<<3(%0);"
444       "fld    fs6,  20<<3(%0);"
445       "fld    fs7,  21<<3(%0);"
446       "fld    fs8,  22<<3(%0);"
447       "fld    fs9,  23<<3(%0);"
448       "fld    fs10, 24<<3(%0);"
449       "fld    fs11, 25<<3(%0);"
450 #      elif __riscv_float_abi_soft
451 #      else
452 #        error "Unsupported case"
453 #      endif
454       "ld     a4, 13<<3(%0);"
455       "mv     sp, a4;"
456       // Return the value requested to return through arguments.
457       // This should be in x11 given what we requested above.
458       "seqz   a0, %1;"
459       "add    a0, a0, %1;"
460       "ret;"
461       : "+r"(env_address)
462       : "r"(retval_tmp));
463 #    endif
464 }
465 
466 INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {
467   if (env[0].__magic != kHwJmpBufMagic) {
468     Printf(
469         "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
470         "there is a bug in HWASan.\n");
471     return REAL(siglongjmp)(env, val);
472   }
473 
474   if (env[0].__mask_was_saved)
475     // Restore the saved signal mask.
476     (void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask, (__hw_sigset_t *)0);
477   InternalLongjmp(env[0].__jmpbuf, val);
478 }
479 
480 // Required since glibc libpthread calls __libc_longjmp on pthread_exit, and
481 // _setjmp on start_thread.  Hence we have to intercept the longjmp on
482 // pthread_exit so the __hw_jmp_buf order matches.
483 INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {
484   if (env[0].__magic != kHwJmpBufMagic)
485     return REAL(__libc_longjmp)(env, val);
486   InternalLongjmp(env[0].__jmpbuf, val);
487 }
488 
489 INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {
490   if (env[0].__magic != kHwJmpBufMagic) {
491     Printf(
492         "WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "
493         "there is a bug in HWASan.\n");
494     return REAL(longjmp)(env, val);
495   }
496   InternalLongjmp(env[0].__jmpbuf, val);
497 }
498 #    undef SIG_BLOCK
499 #    undef SIG_SETMASK
500 
501 #  endif  // HWASAN_WITH_INTERCEPTORS
502 
503 namespace __hwasan {
504 
505 int OnExit() {
506   if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks &&
507       __lsan::HasReportedLeaks()) {
508     return common_flags()->exitcode;
509   }
510   // FIXME: ask frontend whether we need to return failure.
511   return 0;
512 }
513 
514 }  // namespace __hwasan
515 
516 namespace __hwasan {
517 
518 void InitializeInterceptors() {
519   static int inited = 0;
520   CHECK_EQ(inited, 0);
521 
522 #  if HWASAN_WITH_INTERCEPTORS
523   InitializeCommonInterceptors();
524 
525   (void)(read_iovec);
526   (void)(write_iovec);
527 
528 #    if defined(__linux__)
529   INTERCEPT_FUNCTION(__libc_longjmp);
530   INTERCEPT_FUNCTION(longjmp);
531   INTERCEPT_FUNCTION(siglongjmp);
532   INTERCEPT_FUNCTION(vfork);
533 #    endif  // __linux__
534   INTERCEPT_FUNCTION(pthread_create);
535   INTERCEPT_FUNCTION(pthread_join);
536   INTERCEPT_FUNCTION(pthread_detach);
537   INTERCEPT_FUNCTION(pthread_exit);
538 #    if SANITIZER_GLIBC
539   INTERCEPT_FUNCTION(pthread_tryjoin_np);
540   INTERCEPT_FUNCTION(pthread_timedjoin_np);
541 #    endif
542 #  endif
543 
544   inited = 1;
545 }
546 }  // namespace __hwasan
547 
548 #endif  // #if !SANITIZER_FUCHSIA
549