diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2014-11-06 22:49:13 +0000 |
commit | 8ef50bf3d1c287b5013c3168de77a462dfce3495 (patch) | |
tree | 3467f3372c1195b1546172d89af2205a50b1866d /lib/msan/msan_interceptors.cc | |
parent | 11023dc647fd8f41418da90d59db138400d0f334 (diff) | |
download | src-8ef50bf3d1c287b5013c3168de77a462dfce3495.tar.gz src-8ef50bf3d1c287b5013c3168de77a462dfce3495.zip |
Import compiler-rt release_34 branch r197381.vendor/compiler-rt/compiler-rt-r197381
Notes
Notes:
svn path=/vendor/compiler-rt/dist/; revision=274201
svn path=/vendor/compiler-rt/compiler-rt-r197381/; revision=274202; tag=vendor/compiler-rt/compiler-rt-r197381
Diffstat (limited to 'lib/msan/msan_interceptors.cc')
-rw-r--r-- | lib/msan/msan_interceptors.cc | 707 |
1 files changed, 523 insertions, 184 deletions
diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 1bcf93db9440..15a8bec12f31 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -19,6 +19,8 @@ #include "msan.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_libc.h" @@ -28,10 +30,15 @@ // ACHTUNG! No other system header includes in this file. // Ideally, we should get rid of stdarg.h as well. -extern "C" const int __msan_keep_going; - using namespace __msan; +using __sanitizer::memory_order; +using __sanitizer::atomic_load; +using __sanitizer::atomic_store; +using __sanitizer::atomic_uintptr_t; + +static unsigned g_thread_finalize_key; + // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; @@ -52,28 +59,30 @@ bool IsInInterceptorScope() { } while (0) // Check that [x, x+n) range is unpoisoned. -#define CHECK_UNPOISONED_0(x, n) \ - do { \ - sptr offset = __msan_test_shadow(x, n); \ - if (__msan::IsInSymbolizer()) break; \ - if (offset >= 0 && __msan::flags()->report_umrs) { \ - GET_CALLER_PC_BP_SP; \ - (void) sp; \ - Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ - offset, x, n); \ - __msan::PrintWarningWithOrigin(pc, bp, \ - __msan_get_origin((char *) x + offset)); \ - if (!__msan_keep_going) { \ - Printf("Exiting\n"); \ - Die(); \ - } \ - } \ +#define CHECK_UNPOISONED_0(x, n) \ + do { \ + sptr offset = __msan_test_shadow(x, n); \ + if (__msan::IsInSymbolizer()) break; \ + if (offset >= 0 && __msan::flags()->report_umrs) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ + offset, x, n); \ + __msan::PrintWarningWithOrigin(pc, bp, \ + __msan_get_origin((char *)x + offset)); \ + if (__msan::flags()->halt_on_error) { \ + Printf("Exiting\n"); \ + Die(); \ + } \ + } \ } while (0) // Check that [x, x+n) range is unpoisoned unless we are in a nested // interceptor. -#define CHECK_UNPOISONED(x, n) \ - if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED(x, n) \ + do { \ + if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ + } while (0); static void *fast_memset(void *ptr, int c, SIZE_T n); static void *fast_memcpy(void *dst, const void *src, SIZE_T n); @@ -103,22 +112,22 @@ INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { return res; } -INTERCEPTOR(void *, readdir, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent_sz); - return res; +INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { + return __msan_memcpy(dest, src, n); } -INTERCEPTOR(void *, readdir64, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent64_sz); - return res; +INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { + return (char *)__msan_memcpy(dest, src, n) + n; } -INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { - return __msan_memcpy(dest, src, n); +INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = REAL(memccpy)(dest, src, c, n); + CHECK(!res || (res >= dest && res <= (char *)dest + n)); + SIZE_T sz = res ? (char *)res - (char *)dest : n; + CHECK_UNPOISONED(src, sz); + __msan_unpoison(dest, sz); + return res; } INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) { @@ -129,6 +138,10 @@ INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) { return __msan_memset(s, c, n); } +INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { + return __msan_memmove(dest, src, n); +} + INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); @@ -139,10 +152,35 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { return 0; } +INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + CHECK_EQ(boundary & (boundary - 1), 0); + void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + return ptr; +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); + return ptr; +} + +INTERCEPTOR(void *, pvalloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); + return ptr; +} + INTERCEPTOR(void, free, void *ptr) { - ENSURE_MSAN_INITED(); + GET_MALLOC_STACK_TRACE; if (ptr == 0) return; - MsanDeallocate(ptr); + MsanDeallocate(&stack, ptr); } INTERCEPTOR(SIZE_T, strlen, const char *s) { @@ -181,6 +219,14 @@ INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT return res; } +INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(stpcpy)(dest, src); // NOLINT + __msan_copy_poison(dest, src, n + 1); + return res; +} + INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); SIZE_T n = REAL(strlen)(src); @@ -295,6 +341,26 @@ INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(double, strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, __strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(__strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); float res = REAL(strtof)(nptr, endptr); // NOLINT @@ -304,6 +370,26 @@ INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(float, strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, __strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(__strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); long double res = REAL(strtold)(nptr, endptr); // NOLINT @@ -313,11 +399,50 @@ INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(long double, strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, __strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(__strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vasprintf)(strp, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(strp, sizeof(*strp)); + __msan_unpoison(*strp, res + 1); + } + return res; +} + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) { // NOLINT + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vasprintf(strp, format, ap); // NOLINT + va_end(ap); + return res; +} + INTERCEPTOR(int, vsnprintf, char *str, uptr size, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsnprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -326,7 +451,7 @@ INTERCEPTOR(int, vsnprintf, char *str, uptr size, INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsprintf)(str, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -335,7 +460,7 @@ INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vswprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, 4 * (res + 1)); } return res; @@ -370,25 +495,24 @@ INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { // SIZE_T strftime(char *s, SIZE_T max, const char *format,const struct tm *tm); INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, - void *tm) { + __sanitizer_tm *tm) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(strftime)(s, max, format, tm); if (res) __msan_unpoison(s, res + 1); return res; } -INTERCEPTOR(SIZE_T, wcstombs, void *dest, void *src, SIZE_T size) { +INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(wcstombs)(dest, src, size); - if (res != (SIZE_T)-1) __msan_unpoison(dest, res + 1); + int res = REAL(mbtowc)(dest, src, n); + if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } -// SIZE_T mbstowcs(wchar_t *dest, const char *src, SIZE_T n); -INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T n) { +INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(mbstowcs)(dest, src, n); - if (res != (SIZE_T)-1) __msan_unpoison(dest, (res + 1) * sizeof(wchar_t)); + SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); + if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } @@ -422,6 +546,13 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { return res; } +INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmempcpy)(dest, src, n); + __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); @@ -450,28 +581,6 @@ INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) { return res; } -// #define UNSUPPORTED(name) \ -// INTERCEPTOR(void, name, void) { \ -// Printf("MSAN: Unsupported %s\n", __FUNCTION__); \ -// Die(); \ -// } - -// FIXME: intercept the following functions: -// Note, they only matter when running without a dynamic tool. -// UNSUPPORTED(wcscoll_l) -// UNSUPPORTED(wcsnrtombs) -// UNSUPPORTED(wcstol) -// UNSUPPORTED(wcstoll) -// UNSUPPORTED(wcstold) -// UNSUPPORTED(wcstoul) -// UNSUPPORTED(wcstoull) -// UNSUPPORTED(wcsxfrm_l) -// UNSUPPORTED(wcsdup) -// UNSUPPORTED(wcsftime) -// UNSUPPORTED(wcsstr) -// UNSUPPORTED(wcsrchr) -// UNSUPPORTED(wctob) - INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); @@ -502,6 +611,32 @@ INTERCEPTOR(char *, getenv, char *name) { return res; } +extern char **environ; + +static void UnpoisonEnviron() { + char **envp = environ; + for (; *envp; ++envp) { + __msan_unpoison(envp, sizeof(*envp)); + __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); + } + // Trailing NULL pointer. + __msan_unpoison(envp, sizeof(*envp)); +} + +INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { + ENSURE_MSAN_INITED(); + int res = REAL(setenv)(name, value, overwrite); + if (!res) UnpoisonEnviron(); + return res; +} + +INTERCEPTOR(int, putenv, char *string) { + ENSURE_MSAN_INITED(); + int res = REAL(putenv)(string); + if (!res) UnpoisonEnviron(); + return res; +} + INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); @@ -518,6 +653,22 @@ INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { return res; } +INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); @@ -592,22 +743,6 @@ INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { return res; } -INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { - ENSURE_MSAN_INITED(); - char *res = REAL(getcwd)(buf, size); - if (res) - __msan_unpoison(res, REAL(strlen)(res) + 1); - return res; -} - -INTERCEPTOR(char *, realpath, char *path, char *abspath) { - ENSURE_MSAN_INITED(); - char *res = REAL(realpath)(path, abspath); - if (res) - __msan_unpoison(abspath, REAL(strlen)(abspath) + 1); - return res; -} - INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit)(resource, rlim); @@ -628,38 +763,6 @@ INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { return res; } -INTERCEPTOR(int, statfs, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, fstatfs, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, statfs64, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs64)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - -INTERCEPTOR(int, fstatfs64, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs64)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - INTERCEPTOR(int, uname, void *utsname) { ENSURE_MSAN_INITED(); int res = REAL(uname)(utsname); @@ -710,35 +813,24 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, - void *srcaddr, void *addrlen) { + void *srcaddr, int *addrlen) { ENSURE_MSAN_INITED(); SIZE_T srcaddr_sz; - if (srcaddr) - srcaddr_sz = __sanitizer_get_socklen_t(addrlen); + if (srcaddr) srcaddr_sz = *addrlen; SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { __msan_unpoison(buf, res); if (srcaddr) { - SIZE_T sz = __sanitizer_get_socklen_t(addrlen); + SIZE_T sz = *addrlen; __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); } } return res; } -INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct msghdr *msg, int flags) { - ENSURE_MSAN_INITED(); - SSIZE_T res = REAL(recvmsg)(fd, msg, flags); - if (res > 0) { - for (SIZE_T i = 0; i < __sanitizer_get_msghdr_iovlen(msg); ++i) - __msan_unpoison(__sanitizer_get_msghdr_iov_iov_base(msg, i), - __sanitizer_get_msghdr_iov_iov_len(msg, i)); - } - return res; -} - INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); GET_MALLOC_STACK_TRACE; if (!msan_inited) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. @@ -814,6 +906,13 @@ INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { return res; } +INTERCEPTOR(char *, dlerror) { + ENSURE_MSAN_INITED(); + char *res = REAL(dlerror)(); + if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); + return res; +} + // dlopen() ultimately calls mmap() down inside the loader, which generally // doesn't participate in dynamic symbol resolution. Therefore we won't // intercept its calls to mmap, and we have to hook it here. The loader @@ -828,7 +927,7 @@ INTERCEPTOR(void *, dlopen, const char *filename, int flag) { if (!__msan_has_dynamic_component() && map) { // If msandr didn't clear the shadow before the initializers ran, we do it // ourselves afterwards. - UnpoisonMappedDSO(map); + ForEachMappedRegion(map, __msan_unpoison); } return (void *)map; } @@ -848,7 +947,7 @@ static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; - __msan_unpoison_param(3); + UnpoisonParam(3); return cbdata->callback(info, size, cbdata->data); } @@ -872,22 +971,32 @@ INTERCEPTOR(int, getrusage, int who, void *usage) { return res; } +// sigactions_mu guarantees atomicity of sigaction() and signal() calls. +// Access to sigactions[] is gone with relaxed atomics to avoid data race with +// the signal handler. const int kMaxSignals = 1024; -static uptr sigactions[kMaxSignals]; +static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(1); + typedef void (*signal_cb)(int x); - signal_cb cb = (signal_cb)sigactions[signo]; + signal_cb cb = + (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo); } static void SignalAction(int signo, void *si, void *uc) { - __msan_unpoison(si, __sanitizer::struct_sigaction_sz); + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(3); + __msan_unpoison(si, sizeof(__sanitizer_sigaction)); __msan_unpoison(uc, __sanitizer::ucontext_t_sz); typedef void (*sigaction_cb)(int, void *, void *); - sigaction_cb cb = (sigaction_cb)sigactions[signo]; + sigaction_cb cb = + (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo, si, uc); } @@ -900,25 +1009,25 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, if (flags()->wrap_signals) { SpinMutexLock lock(&sigactions_mu); CHECK_LT(signo, kMaxSignals); - uptr old_cb = sigactions[signo]; + uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : 0; if (act) { - internal_memcpy(pnew_act, act, __sanitizer::struct_sigaction_sz); - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(pnew_act); - uptr new_cb = - __sanitizer::__sanitizer_get_sigaction_sa_siginfo(pnew_act) ? - (uptr)SignalAction : (uptr)SignalHandler; + internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction)); + uptr cb = (uptr)pnew_act->sa_sigaction; + uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) + ? (uptr)SignalAction + : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(pnew_act, new_cb); + atomic_store(&sigactions[signo], cb, memory_order_relaxed); + pnew_act->sa_sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(oldact); + uptr cb = (uptr)oldact->sa_sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(oldact, old_cb); + oldact->sa_sigaction = (void (*)(int, void *, void *))old_cb; } } } else { @@ -926,7 +1035,7 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, } if (res == 0 && oldact) { - __msan_unpoison(oldact, __sanitizer::struct_sigaction_sz); + __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); } return res; } @@ -937,7 +1046,7 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { CHECK_LT(signo, kMaxSignals); SpinMutexLock lock(&sigactions_mu); if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; + atomic_store(&sigactions[signo], cb, memory_order_relaxed); cb = (uptr) SignalHandler; } return REAL(signal)(signo, cb); @@ -948,8 +1057,39 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstack(void *attr, uptr *stack, uptr *stacksize); +extern "C" int pthread_setspecific(unsigned key, const void *v); +extern "C" int pthread_yield(); + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + return; + } + MsanAllocatorThreadFinish(); +} + +struct ThreadParam { + void* (*callback)(void *arg); + void *param; + atomic_uintptr_t done; +}; + +static void *MsanThreadStartFunc(void *arg) { + ThreadParam *p = (ThreadParam *)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + atomic_store(&p->done, 1, memory_order_release); + return callback(param); +} INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { @@ -960,9 +1100,19 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), attr = &myattr; } - AdjustStackSizeLinux(attr, flags()->verbosity); + AdjustStackSizeLinux(attr); + + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.done, 0, memory_order_relaxed); + + int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, (void *)&p); + if (res == 0) { + while (atomic_load(&p.done, memory_order_acquire) != 1) + pthread_yield(); + } - int res = REAL(pthread_create)(th, attr, callback, param); if (attr == &myattr) pthread_attr_destroy(&myattr); if (!res) { @@ -971,37 +1121,177 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), return res; } +INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, + void (*dtor)(void *value)) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_key_create)(key, dtor); + if (!res && key) + __msan_unpoison(key, sizeof(*key)); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **retval) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_join)(th, retval); + if (!res && retval) + __msan_unpoison(retval, sizeof(*retval)); + return res; +} + +extern char *tzname[2]; + +INTERCEPTOR(void, tzset) { + ENSURE_MSAN_INITED(); + REAL(tzset)(); + if (tzname[0]) + __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); + if (tzname[1]) + __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); + return; +} + +struct MSanAtExitRecord { + void (*func)(void *arg); + void *arg; +}; + +void MSanAtExitWrapper(void *arg) { + UnpoisonParam(1); + MSanAtExitRecord *r = (MSanAtExitRecord *)arg; + r->func(r->arg); + InternalFree(r); +} + +// Unpoison argument shadow for C++ module destructors. +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); + ENSURE_MSAN_INITED(); + MSanAtExitRecord *r = + (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); + r->func = func; + r->arg = arg; + return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); +} + +DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) + +INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { + ENSURE_MSAN_INITED(); + void *p = REAL(shmat)(shmid, shmaddr, shmflg); + if (p != (void *)-1) { + __sanitizer_shmid_ds ds; + int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); + if (!res) { + __msan_unpoison(p, ds.shm_segsz); + } + } + return p; +} + +// Linux kernel has a bug that leads to kernel deadlock if a process +// maps TBs of memory and then calls mlock(). +static void MlockIsUnsupported() { + static atomic_uint8_t printed; + if (atomic_exchange(&printed, 1, memory_order_relaxed)) + return; + if (common_flags()->verbosity > 0) + Printf("INFO: MemorySanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + struct MSanInterceptorContext { bool in_interceptor_scope; }; -// A version of CHECK_UNPOISED using a saved scope value. Used in common +namespace __msan { + +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __msan + +extern "C" int *__errno_location(void); + +// A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. -#define CHECK_UNPOISONED_CTX(ctx, x, n) \ - if (!((MSanInterceptorContext *) ctx)->in_interceptor_scope) \ - CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED_CTX(ctx, x, n) \ + do { \ + if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ + CHECK_UNPOISONED_0(x, n); \ + } while (0) +#define MSAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("MemorySanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + UnpoisonParam(count) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - CHECK_UNPOISONED_CTX(ctx, ptr, size); -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ - MSanInterceptorContext msan_ctx = { IsInInterceptorScope() }; \ - ctx = (void *)&msan_ctx; \ - InterceptorScope interceptor_scope; \ + CHECK_UNPOISONED_CTX(ctx, ptr, size) +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, ptr, size) \ + __msan_unpoison(ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ + MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ + ctx = (void *)&msan_ctx; \ + (void)ctx; \ + InterceptorScope interceptor_scope; \ + __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ ENSURE_MSAN_INITED(); #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - do { } while (false) // FIXME + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" @@ -1062,15 +1352,41 @@ void __msan_clear_and_unpoison(void *a, uptr size) { fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); } +u32 get_origin_if_poisoned(uptr a, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(a); + for (uptr i = 0; i < size; ++i) + if (s[i]) + return *(u32 *)SHADOW_TO_ORIGIN((s + i) & ~3UL); + return 0; +} + void __msan_copy_origin(void *dst, const void *src, uptr size) { if (!__msan_get_track_origins()) return; if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; - uptr d = MEM_TO_ORIGIN(dst); - uptr s = MEM_TO_ORIGIN(src); - uptr beg = d & ~3UL; // align down. - uptr end = (d + size + 3) & ~3UL; // align up. - s = s & ~3UL; // align down. - fast_memcpy((void*)beg, (void*)s, end - beg); + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = get_origin_if_poisoned(beg, d - beg); + if (o) + *(u32 *)MEM_TO_ORIGIN(beg) = o; + beg += 4; + } + + uptr end = (d + size + 3) & ~3UL; + // Copy right unaligned origin if that memory is poisoned. + if (end > d + size) { + u32 o = get_origin_if_poisoned(d + size, end - d - size); + if (o) + *(u32 *)MEM_TO_ORIGIN(end - 4) = o; + end -= 4; + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + fast_memcpy((void*)MEM_TO_ORIGIN(beg), (void*)MEM_TO_ORIGIN(s), end - beg); + } } void __msan_copy_poison(void *dst, const void *src, uptr size) { @@ -1119,6 +1435,9 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(mmap); INTERCEPT_FUNCTION(mmap64); INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); @@ -1126,15 +1445,18 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(fread); INTERCEPT_FUNCTION(fread_unlocked); INTERCEPT_FUNCTION(readlink); - INTERCEPT_FUNCTION(readdir); - INTERCEPT_FUNCTION(readdir64); INTERCEPT_FUNCTION(memcpy); + INTERCEPT_FUNCTION(memccpy); + INTERCEPT_FUNCTION(mempcpy); INTERCEPT_FUNCTION(memset); INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(bcopy); INTERCEPT_FUNCTION(wmemset); INTERCEPT_FUNCTION(wmemcpy); + INTERCEPT_FUNCTION(wmempcpy); INTERCEPT_FUNCTION(wmemmove); INTERCEPT_FUNCTION(strcpy); // NOLINT + INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); INTERCEPT_FUNCTION(__strdup); INTERCEPT_FUNCTION(strndup); @@ -1150,8 +1472,16 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(strtoul); INTERCEPT_FUNCTION(strtoull); INTERCEPT_FUNCTION(strtod); + INTERCEPT_FUNCTION(strtod_l); + INTERCEPT_FUNCTION(__strtod_l); INTERCEPT_FUNCTION(strtof); + INTERCEPT_FUNCTION(strtof_l); + INTERCEPT_FUNCTION(__strtof_l); INTERCEPT_FUNCTION(strtold); + INTERCEPT_FUNCTION(strtold_l); + INTERCEPT_FUNCTION(__strtold_l); + INTERCEPT_FUNCTION(vasprintf); + INTERCEPT_FUNCTION(asprintf); INTERCEPT_FUNCTION(vsprintf); INTERCEPT_FUNCTION(vsnprintf); INTERCEPT_FUNCTION(vswprintf); @@ -1159,20 +1489,24 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(snprintf); INTERCEPT_FUNCTION(swprintf); INTERCEPT_FUNCTION(strftime); - INTERCEPT_FUNCTION(wcstombs); - INTERCEPT_FUNCTION(mbstowcs); + INTERCEPT_FUNCTION(mbtowc); + INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(wcstod); INTERCEPT_FUNCTION(getenv); + INTERCEPT_FUNCTION(setenv); + INTERCEPT_FUNCTION(putenv); INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); INTERCEPT_FUNCTION(__fxstat); + INTERCEPT_FUNCTION(__fxstatat); INTERCEPT_FUNCTION(__xstat); INTERCEPT_FUNCTION(__lxstat); INTERCEPT_FUNCTION(__fxstat64); + INTERCEPT_FUNCTION(__fxstatat64); INTERCEPT_FUNCTION(__xstat64); INTERCEPT_FUNCTION(__lxstat64); INTERCEPT_FUNCTION(pipe); @@ -1180,28 +1514,33 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); INTERCEPT_FUNCTION(fgets_unlocked); - INTERCEPT_FUNCTION(getcwd); - INTERCEPT_FUNCTION(realpath); INTERCEPT_FUNCTION(getrlimit); INTERCEPT_FUNCTION(getrlimit64); - INTERCEPT_FUNCTION(statfs); - INTERCEPT_FUNCTION(fstatfs); - INTERCEPT_FUNCTION(statfs64); - INTERCEPT_FUNCTION(fstatfs64); INTERCEPT_FUNCTION(uname); INTERCEPT_FUNCTION(gethostname); INTERCEPT_FUNCTION(epoll_wait); INTERCEPT_FUNCTION(epoll_pwait); INTERCEPT_FUNCTION(recv); INTERCEPT_FUNCTION(recvfrom); - INTERCEPT_FUNCTION(recvmsg); INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dlopen); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); INTERCEPT_FUNCTION(signal); INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_key_create); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(tzset); + INTERCEPT_FUNCTION(__cxa_atexit); + INTERCEPT_FUNCTION(shmat); + + if (REAL(pthread_key_create)(&g_thread_finalize_key, &thread_finalize)) { + Printf("MemorySanitizer: failed to create thread key\n"); + Die(); + } + inited = 1; } } // namespace __msan |