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