1 //===-- hwasan_interceptors.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of HWAddressSanitizer.
11 // Interceptors for standard library functions.
13 // FIXME: move as many interceptors as possible into
14 // sanitizer_common/sanitizer_common_interceptors.h
15 //===----------------------------------------------------------------------===//
17 #define SANITIZER_COMMON_NO_REDEFINE_BUILTINS
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"
31 #if !SANITIZER_FUCHSIA
33 using namespace __hwasan
;
35 struct HWAsanInterceptorContext
{
36 const char *interceptor_name
;
39 # define ACCESS_MEMORY_RANGE(ctx, offset, size, access) \
41 __hwasan::CheckAddressSized<ErrorAction::Abort, access>((uptr)offset, \
45 # define HWASAN_READ_RANGE(ctx, offset, size) \
46 ACCESS_MEMORY_RANGE(ctx, offset, size, AccessType::Load)
47 # define HWASAN_WRITE_RANGE(ctx, offset, size) \
48 ACCESS_MEMORY_RANGE(ctx, offset, size, AccessType::Store)
51 # define HWASAN_INTERCEPT_FUNC(name) \
53 if (!INTERCEPT_FUNCTION(name)) \
54 VReport(1, "HWAddressSanitizer: failed to intercept '%s'\n", #name); \
56 # define HWASAN_INTERCEPT_FUNC_VER(name, ver) \
58 if (!INTERCEPT_FUNCTION_VER(name, ver)) \
59 VReport(1, "HWAddressSanitizer: failed to intercept '%s@@%s'\n", \
62 # define HWASAN_INTERCEPT_FUNC_VER_UNVERSIONED_FALLBACK(name, ver) \
64 if (!INTERCEPT_FUNCTION_VER(name, ver) && !INTERCEPT_FUNCTION(name)) \
66 1, "HWAddressSanitizer: failed to intercept '%s@@%s' or '%s'\n", \
71 // OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.
72 # define HWASAN_INTERCEPT_FUNC(name)
73 # endif // SANITIZER_APPLE
75 # if HWASAN_WITH_INTERCEPTORS
77 # define COMMON_SYSCALL_PRE_READ_RANGE(p, s) __hwasan_loadN((uptr)p, (uptr)s)
78 # define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \
79 __hwasan_storeN((uptr)p, (uptr)s)
80 # define COMMON_SYSCALL_POST_READ_RANGE(p, s) \
85 # define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \
90 # include "sanitizer_common/sanitizer_common_syscalls.inc"
91 # include "sanitizer_common/sanitizer_syscalls_netbsd.inc"
93 # define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \
94 HWASAN_WRITE_RANGE(ctx, ptr, size)
96 # define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \
97 HWASAN_READ_RANGE(ctx, ptr, size)
99 # define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \
100 HWAsanInterceptorContext _ctx = {#func}; \
101 ctx = (void *)&_ctx; \
107 # define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \
113 # define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \
119 # define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \
125 # define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \
132 # define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \
138 # define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \
145 # define COMMON_INTERCEPTOR_BLOCK_REAL(name) \
150 # define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \
152 if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \
153 return internal_memset(dst, v, size); \
154 COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \
155 if (MemIsApp(UntagAddr(reinterpret_cast<uptr>(dst))) && \
156 common_flags()->intercept_intrin) \
157 COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \
158 return REAL(memset)(dst, v, size); \
161 # define COMMON_INTERCEPTOR_STRERROR() \
165 # define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)
167 # define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!hwasan_inited)
169 // The main purpose of the mmap interceptor is to prevent the user from
170 // allocating on top of shadow pages.
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
) {
181 if (flags
& map_fixed
) CHECK_EQ(addr
, UntagPtr(addr
));
183 addr
= UntagPtr(addr
);
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
;
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
;
210 __hwasan::TagMemoryAligned(beg
, rounded_length
, 0);
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
;
228 __hwasan::TagMemoryAligned(beg
, rounded_length
, 0);
230 return real_munmap(addr
, length
);
233 # define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, \
237 return mmap_interceptor(REAL(mmap), addr, sz, prot, flags, fd, off); \
240 # define COMMON_INTERCEPTOR_MUNMAP_IMPL(ctx, addr, length) \
243 return munmap_interceptor(REAL(munmap), addr, sz); \
246 # include "sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc"
247 # include "sanitizer_common/sanitizer_common_interceptors.inc"
249 struct ThreadStartArg
{
250 __sanitizer_sigset_t starting_sigset_
;
253 static void *HwasanThreadStartFunc(void *arg
) {
254 __hwasan_thread_enter();
255 SetSigProcMask(&reinterpret_cast<ThreadStartArg
*>(arg
)->starting_sigset_
,
258 auto self
= GetThreadSelf();
259 auto args
= hwasanThreadArgRetval().GetArgs(self
);
260 void *retval
= (*args
.routine
)(args
.arg_retval
);
261 hwasanThreadArgRetval().Finish(self
, retval
);
266 int pthread_attr_getdetachstate(void *attr
, int *v
);
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
]() {
275 return attr
&& !pthread_attr_getdetachstate(attr
, &d
) && IsStateDetached(d
);
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
;
285 hwasanThreadArgRetval().Create(detached
, {callback
, param
}, [&]() -> uptr
{
286 result
= REAL(pthread_create
)(thread
, attr
, &HwasanThreadStartFunc
, A
);
287 return result
? 0 : *(uptr
*)(thread
);
294 INTERCEPTOR(int, pthread_join
, void *thread
, void **retval
) {
296 hwasanThreadArgRetval().Join((uptr
)thread
, [&]() {
297 result
= REAL(pthread_join
)(thread
, retval
);
303 INTERCEPTOR(int, pthread_detach
, void *thread
) {
305 hwasanThreadArgRetval().Detach((uptr
)thread
, [&]() {
306 result
= REAL(pthread_detach
)(thread
);
312 INTERCEPTOR(void, pthread_exit
, void *retval
) {
313 hwasanThreadArgRetval().Finish(GetThreadSelf(), retval
);
314 REAL(pthread_exit
)(retval
);
318 INTERCEPTOR(int, pthread_tryjoin_np
, void *thread
, void **ret
) {
320 hwasanThreadArgRetval().Join((uptr
)thread
, [&]() {
321 result
= REAL(pthread_tryjoin_np
)(thread
, ret
);
327 INTERCEPTOR(int, pthread_timedjoin_np
, void *thread
, void **ret
,
328 const struct timespec
*abstime
) {
330 hwasanThreadArgRetval().Join((uptr
)thread
, [&]() {
331 result
= REAL(pthread_timedjoin_np
)(thread
, ret
, abstime
);
338 DEFINE_REAL_PTHREAD_FUNCTIONS
340 DEFINE_REAL(int, vfork
)
341 DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork
)
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
);
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
=
352 sigprocmask(SIG_BLOCK
, (__hw_sigset_t
*)0, &env
[0].__saved_mask
) == 0);
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;
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
);
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];
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];"
394 // Return the value requested to return through arguments.
395 // This should be in x1 given what we requested above.
398 "csel x0, %1, x0, ne;"
402 # elif defined(__x86_64__)
403 register long int retval_tmp
asm("%rsi") = retval
;
404 register void *env_address
asm("%rdi") = &env
[0];
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.
419 "jmp *%%rdx;" ::"r"(env_address
),
421 # elif SANITIZER_RISCV64
422 register long int retval_tmp
asm("x11") = retval
;
423 register void *env_address
asm("x10") = &env
[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
453 # error "Unsupported case"
457 // Return the value requested to return through arguments.
458 // This should be in x11 given what we requested above.
467 INTERCEPTOR(void, siglongjmp
, __hw_sigjmp_buf env
, int val
) {
468 if (env
[0].__magic
!= kHwJmpBufMagic
) {
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
);
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
);
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
);
490 INTERCEPTOR(void, longjmp
, __hw_jmp_buf env
, int val
) {
491 if (env
[0].__magic
!= kHwJmpBufMagic
) {
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
);
497 InternalLongjmp(env
[0].__jmpbuf
, val
);
502 # endif // HWASAN_WITH_INTERCEPTORS
507 if (CAN_SANITIZE_LEAKS
&& common_flags()->detect_leaks
&&
508 __lsan::HasReportedLeaks()) {
509 return common_flags()->exitcode
;
511 // FIXME: ask frontend whether we need to return failure.
515 } // namespace __hwasan
519 void InitializeInterceptors() {
520 static int inited
= 0;
523 # if HWASAN_WITH_INTERCEPTORS
524 InitializeCommonInterceptors();
529 # if defined(__linux__)
530 INTERCEPT_FUNCTION(__libc_longjmp
);
531 INTERCEPT_FUNCTION(longjmp
);
532 INTERCEPT_FUNCTION(siglongjmp
);
533 INTERCEPT_FUNCTION(vfork
);
535 INTERCEPT_FUNCTION(pthread_create
);
536 INTERCEPT_FUNCTION(pthread_join
);
537 INTERCEPT_FUNCTION(pthread_detach
);
538 INTERCEPT_FUNCTION(pthread_exit
);
540 INTERCEPT_FUNCTION(pthread_tryjoin_np
);
541 INTERCEPT_FUNCTION(pthread_timedjoin_np
);
547 } // namespace __hwasan
549 #endif // #if !SANITIZER_FUCHSIA