Path: blob/main/contrib/llvm-project/compiler-rt/lib/hwasan/hwasan_interceptors.cpp
35235 views
//===-- hwasan_interceptors.cpp -------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of HWAddressSanitizer.9//10// Interceptors for standard library functions.11//12// FIXME: move as many interceptors as possible into13// sanitizer_common/sanitizer_common_interceptors.h14//===----------------------------------------------------------------------===//1516#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS1718#include "hwasan.h"19#include "hwasan_allocator.h"20#include "hwasan_checks.h"21#include "hwasan_mapping.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"2930#if !SANITIZER_FUCHSIA3132using namespace __hwasan;3334struct HWAsanInterceptorContext {35const char *interceptor_name;36};3738# define ACCESS_MEMORY_RANGE(offset, size, access) \39do { \40__hwasan::CheckAddressSized<ErrorAction::Recover, access>((uptr)offset, \41size); \42} while (0)4344# define HWASAN_READ_RANGE(offset, size) \45ACCESS_MEMORY_RANGE(offset, size, AccessType::Load)46# define HWASAN_WRITE_RANGE(offset, size) \47ACCESS_MEMORY_RANGE(offset, size, AccessType::Store)4849# if !SANITIZER_APPLE50# define HWASAN_INTERCEPT_FUNC(name) \51do { \52if (!INTERCEPT_FUNCTION(name)) \53VReport(1, "HWAddressSanitizer: failed to intercept '%s'\n", #name); \54} while (0)55# define HWASAN_INTERCEPT_FUNC_VER(name, ver) \56do { \57if (!INTERCEPT_FUNCTION_VER(name, ver)) \58VReport(1, "HWAddressSanitizer: failed to intercept '%s@@%s'\n", \59#name, ver); \60} while (0)61# define HWASAN_INTERCEPT_FUNC_VER_UNVERSIONED_FALLBACK(name, ver) \62do { \63if (!INTERCEPT_FUNCTION_VER(name, ver) && !INTERCEPT_FUNCTION(name)) \64VReport( \651, "HWAddressSanitizer: failed to intercept '%s@@%s' or '%s'\n", \66#name, ver, #name); \67} while (0)6869# else70// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.71# define HWASAN_INTERCEPT_FUNC(name)72# endif // SANITIZER_APPLE7374# if HWASAN_WITH_INTERCEPTORS7576# define COMMON_SYSCALL_PRE_READ_RANGE(p, s) HWASAN_READ_RANGE(p, s)77# define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) HWASAN_WRITE_RANGE(p, s)78# define COMMON_SYSCALL_POST_READ_RANGE(p, s) \79do { \80(void)(p); \81(void)(s); \82} while (false)83# define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \84do { \85(void)(p); \86(void)(s); \87} while (false)88# include "sanitizer_common/sanitizer_common_syscalls.inc"89# include "sanitizer_common/sanitizer_syscalls_netbsd.inc"9091# define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \92HWASAN_WRITE_RANGE(ptr, size)9394# define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \95HWASAN_READ_RANGE(ptr, size)9697# define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \98HWAsanInterceptorContext _ctx = {#func}; \99ctx = (void *)&_ctx; \100do { \101(void)(ctx); \102(void)(func); \103} while (false)104105# define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \106do { \107(void)(ctx); \108(void)(path); \109} while (false)110111# define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \112do { \113(void)(ctx); \114(void)(fd); \115} while (false)116117# define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \118do { \119(void)(ctx); \120(void)(fd); \121} while (false)122123# define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \124do { \125(void)(ctx); \126(void)(fd); \127(void)(newfd); \128} while (false)129130# define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \131do { \132(void)(ctx); \133(void)(name); \134} while (false)135136# define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \137do { \138(void)(ctx); \139(void)(thread); \140(void)(name); \141} while (false)142143# define COMMON_INTERCEPTOR_BLOCK_REAL(name) \144do { \145(void)(name); \146} while (false)147148# define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \149{ \150if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \151return internal_memset(dst, v, size); \152COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \153if (MemIsApp(UntagAddr(reinterpret_cast<uptr>(dst))) && \154common_flags()->intercept_intrin) \155COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \156return REAL(memset)(dst, v, size); \157}158159# define COMMON_INTERCEPTOR_STRERROR() \160do { \161} while (false)162163# define COMMON_INTERCEPT_FUNCTION(name) HWASAN_INTERCEPT_FUNC(name)164165# define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!hwasan_inited)166167// The main purpose of the mmap interceptor is to prevent the user from168// allocating on top of shadow pages.169//170// For compatibility, it does not tag pointers, nor does it allow171// MAP_FIXED in combination with a tagged pointer. (Since mmap itself172// will not return a tagged pointer, the tagged pointer must have come173// from elsewhere, such as the secondary allocator, which makes it a174// very odd usecase.)175template <class Mmap>176static void *mmap_interceptor(Mmap real_mmap, void *addr, SIZE_T length,177int prot, int flags, int fd, OFF64_T offset) {178if (addr) {179if (flags & map_fixed) CHECK_EQ(addr, UntagPtr(addr));180181addr = UntagPtr(addr);182}183SIZE_T rounded_length = RoundUpTo(length, GetPageSize());184void *end_addr = (char *)addr + (rounded_length - 1);185if (addr && length &&186(!MemIsApp(reinterpret_cast<uptr>(addr)) ||187!MemIsApp(reinterpret_cast<uptr>(end_addr)))) {188// User requested an address that is incompatible with HWASan's189// memory layout. Use a different address if allowed, else fail.190if (flags & map_fixed) {191errno = errno_EINVAL;192return (void *)-1;193} else {194addr = nullptr;195}196}197void *res = real_mmap(addr, length, prot, flags, fd, offset);198if (length && res != (void *)-1) {199uptr beg = reinterpret_cast<uptr>(res);200DCHECK(IsAligned(beg, GetPageSize()));201if (!MemIsApp(beg) || !MemIsApp(beg + rounded_length - 1)) {202// Application has attempted to map more memory than is supported by203// HWASan. Act as if we ran out of memory.204internal_munmap(res, length);205errno = errno_ENOMEM;206return (void *)-1;207}208__hwasan::TagMemoryAligned(beg, rounded_length, 0);209}210211return res;212}213214template <class Munmap>215static int munmap_interceptor(Munmap real_munmap, void *addr, SIZE_T length) {216// We should not tag if munmap fail, but it's to late to tag after217// real_munmap, as the pages could be mmaped by another thread.218uptr beg = reinterpret_cast<uptr>(addr);219if (length && IsAligned(beg, GetPageSize())) {220SIZE_T rounded_length = RoundUpTo(length, GetPageSize());221// Protect from unmapping the shadow.222if (!MemIsApp(beg) || !MemIsApp(beg + rounded_length - 1)) {223errno = errno_EINVAL;224return -1;225}226__hwasan::TagMemoryAligned(beg, rounded_length, 0);227}228return real_munmap(addr, length);229}230231# define COMMON_INTERCEPTOR_MMAP_IMPL(ctx, mmap, addr, length, prot, flags, \232fd, offset) \233do { \234(void)(ctx); \235return mmap_interceptor(REAL(mmap), addr, sz, prot, flags, fd, off); \236} while (false)237238# define COMMON_INTERCEPTOR_MUNMAP_IMPL(ctx, addr, length) \239do { \240(void)(ctx); \241return munmap_interceptor(REAL(munmap), addr, sz); \242} while (false)243244# include "sanitizer_common/sanitizer_common_interceptors_memintrinsics.inc"245# include "sanitizer_common/sanitizer_common_interceptors.inc"246247struct ThreadStartArg {248__sanitizer_sigset_t starting_sigset_;249};250251static void *HwasanThreadStartFunc(void *arg) {252__hwasan_thread_enter();253SetSigProcMask(&reinterpret_cast<ThreadStartArg *>(arg)->starting_sigset_,254nullptr);255InternalFree(arg);256auto self = GetThreadSelf();257auto args = hwasanThreadArgRetval().GetArgs(self);258void *retval = (*args.routine)(args.arg_retval);259hwasanThreadArgRetval().Finish(self, retval);260return retval;261}262263extern "C" {264int pthread_attr_getdetachstate(void *attr, int *v);265}266267INTERCEPTOR(int, pthread_create, void *thread, void *attr,268void *(*callback)(void *), void *param) {269EnsureMainThreadIDIsCorrect();270ScopedTaggingDisabler tagging_disabler;271bool detached = [attr]() {272int d = 0;273return attr && !pthread_attr_getdetachstate(attr, &d) && IsStateDetached(d);274}();275ThreadStartArg *A = (ThreadStartArg *)InternalAlloc(sizeof(ThreadStartArg));276ScopedBlockSignals block(&A->starting_sigset_);277// ASAN uses the same approach to disable leaks from pthread_create.278# if CAN_SANITIZE_LEAKS279__lsan::ScopedInterceptorDisabler lsan_disabler;280# endif281282int result;283hwasanThreadArgRetval().Create(detached, {callback, param}, [&]() -> uptr {284result = REAL(pthread_create)(thread, attr, &HwasanThreadStartFunc, A);285return result ? 0 : *(uptr *)(thread);286});287if (result != 0)288InternalFree(A);289return result;290}291292INTERCEPTOR(int, pthread_join, void *thread, void **retval) {293int result;294hwasanThreadArgRetval().Join((uptr)thread, [&]() {295result = REAL(pthread_join)(thread, retval);296return !result;297});298return result;299}300301INTERCEPTOR(int, pthread_detach, void *thread) {302int result;303hwasanThreadArgRetval().Detach((uptr)thread, [&]() {304result = REAL(pthread_detach)(thread);305return !result;306});307return result;308}309310INTERCEPTOR(void, pthread_exit, void *retval) {311hwasanThreadArgRetval().Finish(GetThreadSelf(), retval);312REAL(pthread_exit)(retval);313}314315# if SANITIZER_GLIBC316INTERCEPTOR(int, pthread_tryjoin_np, void *thread, void **ret) {317int result;318hwasanThreadArgRetval().Join((uptr)thread, [&]() {319result = REAL(pthread_tryjoin_np)(thread, ret);320return !result;321});322return result;323}324325INTERCEPTOR(int, pthread_timedjoin_np, void *thread, void **ret,326const struct timespec *abstime) {327int result;328hwasanThreadArgRetval().Join((uptr)thread, [&]() {329result = REAL(pthread_timedjoin_np)(thread, ret, abstime);330return !result;331});332return result;333}334# endif335336DEFINE_INTERNAL_PTHREAD_FUNCTIONS337338DEFINE_REAL(int, vfork,)339DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,)340341// Get and/or change the set of blocked signals.342extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,343__hw_sigset_t *__restrict __oset);344# define SIG_BLOCK 0345# define SIG_SETMASK 2346extern "C" int __sigjmp_save(__hw_sigjmp_buf env, int savemask) {347env[0].__magic = kHwJmpBufMagic;348env[0].__mask_was_saved =349(savemask &&350sigprocmask(SIG_BLOCK, (__hw_sigset_t *)0, &env[0].__saved_mask) == 0);351return 0;352}353354static void __attribute__((always_inline))355InternalLongjmp(__hw_register_buf env, int retval) {356# if defined(__aarch64__)357constexpr size_t kSpIndex = 13;358# elif defined(__x86_64__)359constexpr size_t kSpIndex = 6;360# elif SANITIZER_RISCV64361constexpr size_t kSpIndex = 13;362# endif363364// Clear all memory tags on the stack between here and where we're going.365unsigned long long stack_pointer = env[kSpIndex];366// The stack pointer should never be tagged, so we don't need to clear the367// tag for this function call.368__hwasan_handle_longjmp((void *)stack_pointer);369370// Run code for handling a longjmp.371// Need to use a register that isn't going to be loaded from the environment372// buffer -- hence why we need to specify the register to use.373// Must implement this ourselves, since we don't know the order of registers374// in different libc implementations and many implementations mangle the375// stack pointer so we can't use it without knowing the demangling scheme.376# if defined(__aarch64__)377register long int retval_tmp asm("x1") = retval;378register void *env_address asm("x0") = &env[0];379asm volatile(380"ldp x19, x20, [%0, #0<<3];"381"ldp x21, x22, [%0, #2<<3];"382"ldp x23, x24, [%0, #4<<3];"383"ldp x25, x26, [%0, #6<<3];"384"ldp x27, x28, [%0, #8<<3];"385"ldp x29, x30, [%0, #10<<3];"386"ldp d8, d9, [%0, #14<<3];"387"ldp d10, d11, [%0, #16<<3];"388"ldp d12, d13, [%0, #18<<3];"389"ldp d14, d15, [%0, #20<<3];"390"ldr x5, [%0, #13<<3];"391"mov sp, x5;"392// Return the value requested to return through arguments.393// This should be in x1 given what we requested above.394"cmp %1, #0;"395"mov x0, #1;"396"csel x0, %1, x0, ne;"397"br x30;"398: "+r"(env_address)399: "r"(retval_tmp));400# elif defined(__x86_64__)401register long int retval_tmp asm("%rsi") = retval;402register void *env_address asm("%rdi") = &env[0];403asm volatile(404// Restore registers.405"mov (0*8)(%0),%%rbx;"406"mov (1*8)(%0),%%rbp;"407"mov (2*8)(%0),%%r12;"408"mov (3*8)(%0),%%r13;"409"mov (4*8)(%0),%%r14;"410"mov (5*8)(%0),%%r15;"411"mov (6*8)(%0),%%rsp;"412"mov (7*8)(%0),%%rdx;"413// Return 1 if retval is 0.414"mov $1,%%rax;"415"test %1,%1;"416"cmovnz %1,%%rax;"417"jmp *%%rdx;" ::"r"(env_address),418"r"(retval_tmp));419# elif SANITIZER_RISCV64420register long int retval_tmp asm("x11") = retval;421register void *env_address asm("x10") = &env[0];422asm volatile(423"ld ra, 0<<3(%0);"424"ld s0, 1<<3(%0);"425"ld s1, 2<<3(%0);"426"ld s2, 3<<3(%0);"427"ld s3, 4<<3(%0);"428"ld s4, 5<<3(%0);"429"ld s5, 6<<3(%0);"430"ld s6, 7<<3(%0);"431"ld s7, 8<<3(%0);"432"ld s8, 9<<3(%0);"433"ld s9, 10<<3(%0);"434"ld s10, 11<<3(%0);"435"ld s11, 12<<3(%0);"436# if __riscv_float_abi_double437"fld fs0, 14<<3(%0);"438"fld fs1, 15<<3(%0);"439"fld fs2, 16<<3(%0);"440"fld fs3, 17<<3(%0);"441"fld fs4, 18<<3(%0);"442"fld fs5, 19<<3(%0);"443"fld fs6, 20<<3(%0);"444"fld fs7, 21<<3(%0);"445"fld fs8, 22<<3(%0);"446"fld fs9, 23<<3(%0);"447"fld fs10, 24<<3(%0);"448"fld fs11, 25<<3(%0);"449# elif __riscv_float_abi_soft450# else451# error "Unsupported case"452# endif453"ld a4, 13<<3(%0);"454"mv sp, a4;"455// Return the value requested to return through arguments.456// This should be in x11 given what we requested above.457"seqz a0, %1;"458"add a0, a0, %1;"459"ret;"460: "+r"(env_address)461: "r"(retval_tmp));462# endif463}464465INTERCEPTOR(void, siglongjmp, __hw_sigjmp_buf env, int val) {466if (env[0].__magic != kHwJmpBufMagic) {467Printf(468"WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "469"there is a bug in HWASan.\n");470return REAL(siglongjmp)(env, val);471}472473if (env[0].__mask_was_saved)474// Restore the saved signal mask.475(void)sigprocmask(SIG_SETMASK, &env[0].__saved_mask, (__hw_sigset_t *)0);476InternalLongjmp(env[0].__jmpbuf, val);477}478479// Required since glibc libpthread calls __libc_longjmp on pthread_exit, and480// _setjmp on start_thread. Hence we have to intercept the longjmp on481// pthread_exit so the __hw_jmp_buf order matches.482INTERCEPTOR(void, __libc_longjmp, __hw_jmp_buf env, int val) {483if (env[0].__magic != kHwJmpBufMagic)484return REAL(__libc_longjmp)(env, val);485InternalLongjmp(env[0].__jmpbuf, val);486}487488INTERCEPTOR(void, longjmp, __hw_jmp_buf env, int val) {489if (env[0].__magic != kHwJmpBufMagic) {490Printf(491"WARNING: Unexpected bad jmp_buf. Either setjmp was not called or "492"there is a bug in HWASan.\n");493return REAL(longjmp)(env, val);494}495InternalLongjmp(env[0].__jmpbuf, val);496}497# undef SIG_BLOCK498# undef SIG_SETMASK499500# endif // HWASAN_WITH_INTERCEPTORS501502namespace __hwasan {503504int OnExit() {505if (CAN_SANITIZE_LEAKS && common_flags()->detect_leaks &&506__lsan::HasReportedLeaks()) {507return common_flags()->exitcode;508}509// FIXME: ask frontend whether we need to return failure.510return 0;511}512513} // namespace __hwasan514515namespace __hwasan {516517void InitializeInterceptors() {518static int inited = 0;519CHECK_EQ(inited, 0);520521# if HWASAN_WITH_INTERCEPTORS522__interception::DoesNotSupportStaticLinking();523InitializeCommonInterceptors();524525(void)(read_iovec);526(void)(write_iovec);527528# if defined(__linux__)529INTERCEPT_FUNCTION(__libc_longjmp);530INTERCEPT_FUNCTION(longjmp);531INTERCEPT_FUNCTION(siglongjmp);532INTERCEPT_FUNCTION(vfork);533# endif // __linux__534INTERCEPT_FUNCTION(pthread_create);535INTERCEPT_FUNCTION(pthread_join);536INTERCEPT_FUNCTION(pthread_detach);537INTERCEPT_FUNCTION(pthread_exit);538# if SANITIZER_GLIBC539INTERCEPT_FUNCTION(pthread_tryjoin_np);540INTERCEPT_FUNCTION(pthread_timedjoin_np);541# endif542# endif543544inited = 1;545}546} // namespace __hwasan547548#endif // #if !SANITIZER_FUCHSIA549550551