550 lines · cpp
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-exception6//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 into14// sanitizer_common/sanitizer_common_interceptors.h15//===----------------------------------------------------------------------===//16 17#define SANITIZER_COMMON_NO_REDEFINE_BUILTINS18 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_FUCHSIA32 33using namespace __hwasan;34 35struct 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_APPLE51# 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# else71// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION.72# define HWASAN_INTERCEPT_FUNC(name)73# endif // SANITIZER_APPLE74 75# if HWASAN_WITH_INTERCEPTORS76 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 from169// allocating on top of shadow pages.170//171// For compatibility, it does not tag pointers, nor does it allow172// MAP_FIXED in combination with a tagged pointer. (Since mmap itself173// will not return a tagged pointer, the tagged pointer must have come174// from elsewhere, such as the secondary allocator, which makes it a175// very odd usecase.)176template <class Mmap>177static 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's190 // 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 by204 // 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 215template <class Munmap>216static 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 after218 // 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 248struct ThreadStartArg {249 __sanitizer_sigset_t starting_sigset_;250};251 252static 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 264extern "C" {265int pthread_attr_getdetachstate(void *attr, int *v);266}267 268INTERCEPTOR(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_LEAKS280 __lsan::ScopedInterceptorDisabler lsan_disabler;281# endif282 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 293INTERCEPTOR(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 302INTERCEPTOR(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 311INTERCEPTOR(void, pthread_exit, void *retval) {312 hwasanThreadArgRetval().Finish(GetThreadSelf(), retval);313 REAL(pthread_exit)(retval);314}315 316# if SANITIZER_GLIBC317INTERCEPTOR(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 326INTERCEPTOR(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# endif336 337DEFINE_INTERNAL_PTHREAD_FUNCTIONS338 339DEFINE_REAL(int, vfork,)340DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(int, vfork,)341 342// Get and/or change the set of blocked signals.343extern "C" int sigprocmask(int __how, const __hw_sigset_t *__restrict __set,344 __hw_sigset_t *__restrict __oset);345# define SIG_BLOCK 0346# define SIG_SETMASK 2347extern "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 355static void __attribute__((always_inline))356InternalLongjmp(__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_RISCV64362 constexpr size_t kSpIndex = 13;363# endif364 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 the368 // 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 environment373 // buffer -- hence why we need to specify the register to use.374 // Must implement this ourselves, since we don't know the order of registers375 // in different libc implementations and many implementations mangle the376 // 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_RISCV64421 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_double438 "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_soft451# else452# error "Unsupported case"453# endif454 "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# endif464}465 466INTERCEPTOR(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, and481// _setjmp on start_thread. Hence we have to intercept the longjmp on482// pthread_exit so the __hw_jmp_buf order matches.483INTERCEPTOR(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 489INTERCEPTOR(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_BLOCK499# undef SIG_SETMASK500 501# endif // HWASAN_WITH_INTERCEPTORS502 503namespace __hwasan {504 505int 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 __hwasan515 516namespace __hwasan {517 518void InitializeInterceptors() {519 static int inited = 0;520 CHECK_EQ(inited, 0);521 522# if HWASAN_WITH_INTERCEPTORS523 __interception::DoesNotSupportStaticLinking();524 InitializeCommonInterceptors();525 526 (void)(read_iovec);527 (void)(write_iovec);528 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_GLIBC540 INTERCEPT_FUNCTION(pthread_tryjoin_np);541 INTERCEPT_FUNCTION(pthread_timedjoin_np);542# endif543# endif544 545 inited = 1;546}547} // namespace __hwasan548 549#endif // #if !SANITIZER_FUCHSIA550