diff options
Diffstat (limited to 'lib/msan')
86 files changed, 2923 insertions, 3013 deletions
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt index 06f3f65d8e38e..90d9face17311 100644 --- a/lib/msan/CMakeLists.txt +++ b/lib/msan/CMakeLists.txt @@ -4,47 +4,43 @@ include_directories(..) set(MSAN_RTL_SOURCES msan.cc msan_allocator.cc + msan_chained_origin_depot.cc msan_interceptors.cc msan_linux.cc msan_new_delete.cc msan_report.cc + msan_thread.cc ) -set(MSAN_RTL_CFLAGS - ${SANITIZER_COMMON_CFLAGS} - -fno-rtti - -fPIE - # Prevent clang from generating libc calls. - -ffreestanding) -# Static runtime library. +set(MSAN_RTL_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_no_rtti_flag(MSAN_RTL_CFLAGS) +append_list_if(COMPILER_RT_HAS_FPIE_FLAG -fPIE MSAN_RTL_CFLAGS) +# Prevent clang from generating libc calls. +append_list_if(COMPILER_RT_HAS_FFREESTANDING_FLAG -ffreestanding MSAN_RTL_CFLAGS) + set(MSAN_RUNTIME_LIBRARIES) -set(arch "x86_64") -if(CAN_TARGET_${arch}) - add_compiler_rt_static_runtime(clang_rt.msan-${arch} ${arch} + +# Static runtime library. +add_custom_target(msan) +foreach(arch ${MSAN_SUPPORTED_ARCH}) + add_compiler_rt_runtime(clang_rt.msan-${arch} ${arch} STATIC SOURCES ${MSAN_RTL_SOURCES} $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> CFLAGS ${MSAN_RTL_CFLAGS}) + add_dependencies(msan clang_rt.msan-${arch}) list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}) if(UNIX) add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra) - list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-${arch}-symbols) + add_dependencies(msan clang_rt.msan-${arch}-symbols) endif() -endif() +endforeach() add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt) +add_dependencies(msan msan_blacklist) +add_dependencies(compiler-rt msan) -# We should only build MSan unit tests if we can build instrumented libcxx. -set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) -if(EXISTS ${MSAN_LIBCXX_PATH}/) - set(MSAN_CAN_INSTRUMENT_LIBCXX TRUE) -else() - set(MSAN_CAN_INSTRUMENT_LIBCXX FALSE) -endif() - -if(LLVM_INCLUDE_TESTS) +if(COMPILER_RT_INCLUDE_TESTS) add_subdirectory(tests) endif() - -add_subdirectory(lit_tests) diff --git a/lib/msan/lit_tests/CMakeLists.txt b/lib/msan/lit_tests/CMakeLists.txt deleted file mode 100644 index 38d1e59e709e2..0000000000000 --- a/lib/msan/lit_tests/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -set(MSAN_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..) -set(MSAN_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/..) - -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) - -if(MSAN_CAN_INSTRUMENT_LIBCXX) - configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) -endif() - -if(COMPILER_RT_CAN_EXECUTE_TESTS AND CAN_TARGET_x86_64) - # Run MSan tests only if we're sure we may produce working binaries. - set(MSAN_TEST_DEPS - ${SANITIZER_COMMON_LIT_TEST_DEPS} - ${MSAN_RUNTIME_LIBRARIES} - msan_blacklist) - set(MSAN_TEST_PARAMS - msan_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) - if(LLVM_INCLUDE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) - list(APPEND MSAN_TEST_DEPS MsanUnitTests) - endif() - add_lit_testsuite(check-msan "Running the MemorySanitizer tests" - ${CMAKE_CURRENT_BINARY_DIR} - PARAMS ${MSAN_TEST_PARAMS} - DEPENDS ${MSAN_TEST_DEPS} - ) - set_target_properties(check-msan PROPERTIES FOLDER "MSan tests") -endif() diff --git a/lib/msan/lit_tests/Linux/glob.cc b/lib/msan/lit_tests/Linux/glob.cc deleted file mode 100644 index 387ce3cf5f1ae..0000000000000 --- a/lib/msan/lit_tests/Linux/glob.cc +++ /dev/null @@ -1,27 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <string.h> -#include <errno.h> - -int main(int argc, char *argv[]) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); - - glob_t globbuf; - int res = glob(buf, 0, 0, &globbuf); - - printf("%d %s\n", errno, strerror(errno)); - assert(res == 0); - assert(globbuf.gl_pathc == 2); - printf("%zu\n", strlen(globbuf.gl_pathv[0])); - printf("%zu\n", strlen(globbuf.gl_pathv[1])); - printf("PASS\n"); - // CHECK: PASS - return 0; -} diff --git a/lib/msan/lit_tests/Linux/glob_altdirfunc.cc b/lib/msan/lit_tests/Linux/glob_altdirfunc.cc deleted file mode 100644 index b8200c3ee8996..0000000000000 --- a/lib/msan/lit_tests/Linux/glob_altdirfunc.cc +++ /dev/null @@ -1,78 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <sys/stat.h> -#include <sys/types.h> -#include <dirent.h> -#include <unistd.h> - -#include <sanitizer/msan_interface.h> - -static void my_gl_closedir(void *dir) { - if (!dir) - exit(1); - closedir((DIR *)dir); -} - -static struct dirent *my_gl_readdir(void *dir) { - if (!dir) - exit(1); - struct dirent *d = readdir((DIR *)dir); - if (d) __msan_poison(d, d->d_reclen); // hehe - return d; -} - -static void *my_gl_opendir(const char *s) { - assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); - return opendir(s); -} - -static int my_gl_lstat(const char *s, struct stat *st) { - assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); - if (!st) - exit(1); - return lstat(s, st); -} - -static int my_gl_stat(const char *s, struct stat *st) { - assert(__msan_test_shadow(s, strlen(s) + 1) == (size_t)-1); - if (!st) - exit(1); - return lstat(s, st); -} - -int main(int argc, char *argv[]) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); - - glob_t globbuf; - globbuf.gl_closedir = my_gl_closedir; - globbuf.gl_readdir = my_gl_readdir; - globbuf.gl_opendir = my_gl_opendir; - globbuf.gl_lstat = my_gl_lstat; - globbuf.gl_stat = my_gl_stat; - for (int i = 0; i < 10000; ++i) { - int res = glob(buf, GLOB_ALTDIRFUNC | GLOB_MARK, 0, &globbuf); - assert(res == 0); - printf("%d %s\n", errno, strerror(errno)); - assert(globbuf.gl_pathc == 2); - printf("%zu\n", strlen(globbuf.gl_pathv[0])); - printf("%zu\n", strlen(globbuf.gl_pathv[1])); - __msan_poison(globbuf.gl_pathv[0], strlen(globbuf.gl_pathv[0]) + 1); - __msan_poison(globbuf.gl_pathv[1], strlen(globbuf.gl_pathv[1]) + 1); - globfree(&globbuf); - } - - printf("PASS\n"); - // CHECK: PASS - return 0; -} diff --git a/lib/msan/lit_tests/Linux/glob_nomatch.cc b/lib/msan/lit_tests/Linux/glob_nomatch.cc deleted file mode 100644 index 0262034aec5bd..0000000000000 --- a/lib/msan/lit_tests/Linux/glob_nomatch.cc +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char *argv[]) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*c"); - - glob_t globbuf; - int res = glob(buf, 0, 0, &globbuf); - assert(res == GLOB_NOMATCH); - assert(globbuf.gl_pathc == 0); - if (globbuf.gl_pathv == 0) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/Linux/glob_test_root/aa b/lib/msan/lit_tests/Linux/glob_test_root/aa deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/Linux/glob_test_root/aa +++ /dev/null diff --git a/lib/msan/lit_tests/Linux/glob_test_root/ab b/lib/msan/lit_tests/Linux/glob_test_root/ab deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/Linux/glob_test_root/ab +++ /dev/null diff --git a/lib/msan/lit_tests/Linux/glob_test_root/ba b/lib/msan/lit_tests/Linux/glob_test_root/ba deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/Linux/glob_test_root/ba +++ /dev/null diff --git a/lib/msan/lit_tests/Linux/lit.local.cfg b/lib/msan/lit_tests/Linux/lit.local.cfg deleted file mode 100644 index 57271b8078a49..0000000000000 --- a/lib/msan/lit_tests/Linux/lit.local.cfg +++ /dev/null @@ -1,9 +0,0 @@ -def getRoot(config): - if not config.parent: - return config - return getRoot(config.parent) - -root = getRoot(config) - -if root.host_os not in ['Linux']: - config.unsupported = True diff --git a/lib/msan/lit_tests/Linux/syscalls.cc b/lib/msan/lit_tests/Linux/syscalls.cc deleted file mode 100644 index ec308bfe30caf..0000000000000 --- a/lib/msan/lit_tests/Linux/syscalls.cc +++ /dev/null @@ -1,100 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t 2>&1 - -#include <assert.h> -#include <errno.h> -#include <glob.h> -#include <stdio.h> -#include <string.h> - -#include <linux/aio_abi.h> -#include <sys/ptrace.h> -#include <sys/stat.h> - -#include <sanitizer/linux_syscall_hooks.h> -#include <sanitizer/msan_interface.h> - -/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general - sanity of their behaviour. */ - -int main(int argc, char *argv[]) { - char buf[1000]; - const int kTen = 10; - const int kFortyTwo = 42; - memset(buf, 0, sizeof(buf)); - __msan_unpoison(buf, sizeof(buf)); - __sanitizer_syscall_pre_recvmsg(0, buf, 0); - __sanitizer_syscall_pre_rt_sigpending(buf, kTen); - __sanitizer_syscall_pre_getdents(0, buf, kTen); - __sanitizer_syscall_pre_getdents64(0, buf, kTen); - - __msan_unpoison(buf, sizeof(buf)); - __sanitizer_syscall_post_recvmsg(0, 0, buf, 0); - __sanitizer_syscall_post_rt_sigpending(-1, buf, kTen); - __sanitizer_syscall_post_getdents(0, 0, buf, kTen); - __sanitizer_syscall_post_getdents64(0, 0, buf, kTen); - assert(__msan_test_shadow(buf, sizeof(buf)) == -1); - - __msan_unpoison(buf, sizeof(buf)); - __sanitizer_syscall_post_recvmsg(kTen, 0, buf, 0); - - // Tell the kernel that the output struct size is 10 bytes, verify that those - // bytes are unpoisoned, and the next byte is not. - __msan_poison(buf, kTen + 1); - __sanitizer_syscall_post_rt_sigpending(0, buf, kTen); - assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); - - __msan_poison(buf, kTen + 1); - __sanitizer_syscall_post_getdents(kTen, 0, buf, kTen); - assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); - - __msan_poison(buf, kTen + 1); - __sanitizer_syscall_post_getdents64(kTen, 0, buf, kTen); - assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); - - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_clock_getres(0, 0, buf); - assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); - - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_clock_gettime(0, 0, buf); - assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); - - // Failed syscall does not write to the buffer. - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_clock_gettime(-1, 0, buf); - assert(__msan_test_shadow(buf, sizeof(buf)) == 0); - - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_read(5, 42, buf, 10); - assert(__msan_test_shadow(buf, sizeof(buf)) == 5); - - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_newfstatat(0, 5, "/path/to/file", buf, 0); - assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(struct stat)); - - __msan_poison(buf, sizeof(buf)); - int prio = 0; - __sanitizer_syscall_post_mq_timedreceive(kFortyTwo, 5, buf, sizeof(buf), &prio, 0); - assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); - assert(__msan_test_shadow(&prio, sizeof(prio)) == -1); - - __msan_poison(buf, sizeof(buf)); - __sanitizer_syscall_post_ptrace(0, PTRACE_PEEKUSER, kFortyTwo, 0xABCD, buf); - assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(void *)); - - __msan_poison(buf, sizeof(buf)); - struct iocb iocb[2]; - struct iocb *iocbp[2] = { &iocb[0], &iocb[1] }; - memset(iocb, 0, sizeof(iocb)); - iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; - iocb[0].aio_buf = (__u64)buf; - iocb[0].aio_nbytes = kFortyTwo; - iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; - iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]); - iocb[1].aio_nbytes = kFortyTwo; - __sanitizer_syscall_post_io_submit(1, 0, 2, &iocbp); - assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); - - return 0; -} diff --git a/lib/msan/lit_tests/Linux/tcgetattr.cc b/lib/msan/lit_tests/Linux/tcgetattr.cc deleted file mode 100644 index e6e101db884f5..0000000000000 --- a/lib/msan/lit_tests/Linux/tcgetattr.cc +++ /dev/null @@ -1,21 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <stdlib.h> -#include <termios.h> -#include <unistd.h> - -int main(int argc, char *argv[]) { - int fd = getpt(); - assert(fd >= 0); - - struct termios t; - int res = tcgetattr(fd, &t); - assert(!res); - - if (t.c_iflag == 0) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc b/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc deleted file mode 100644 index 8930a7159246b..0000000000000 --- a/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include <stdlib.h> - -#include "dso-origin.h" - -void my_access(int *p) { - volatile int tmp; - // Force initialize-ness check. - if (*p) - tmp = 1; -} - -void *my_alloc(unsigned sz) { - return malloc(sz); -} diff --git a/lib/msan/lit_tests/SharedLibs/dso-origin.h b/lib/msan/lit_tests/SharedLibs/dso-origin.h deleted file mode 100644 index ff926b3f61c88..0000000000000 --- a/lib/msan/lit_tests/SharedLibs/dso-origin.h +++ /dev/null @@ -1,4 +0,0 @@ -extern "C" { -void my_access(int *p); -void *my_alloc(unsigned sz); -} diff --git a/lib/msan/lit_tests/SharedLibs/lit.local.cfg b/lib/msan/lit_tests/SharedLibs/lit.local.cfg deleted file mode 100644 index b3677c17a0f2a..0000000000000 --- a/lib/msan/lit_tests/SharedLibs/lit.local.cfg +++ /dev/null @@ -1,4 +0,0 @@ -# Sources in this directory are compiled as shared libraries and used by -# tests in parent directory. - -config.suffixes = [] diff --git a/lib/msan/lit_tests/Unit/lit.site.cfg.in b/lib/msan/lit_tests/Unit/lit.site.cfg.in deleted file mode 100644 index 8e67f557d7fde..0000000000000 --- a/lib/msan/lit_tests/Unit/lit.site.cfg.in +++ /dev/null @@ -1,13 +0,0 @@ -## Autogenerated by LLVM/Clang configuration. -# Do not edit! - -# Load common config for all compiler-rt unit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") - -# Setup config name. -config.name = 'MemorySanitizer-Unit' - -# Setup test source and exec root. For unit tests, we define -# it as build directory with MSan unit tests. -config.test_exec_root = "@MSAN_BINARY_DIR@/tests" -config.test_source_root = config.test_exec_root diff --git a/lib/msan/lit_tests/allocator_returns_null.cc b/lib/msan/lit_tests/allocator_returns_null.cc deleted file mode 100644 index aaa85cce71134..0000000000000 --- a/lib/msan/lit_tests/allocator_returns_null.cc +++ /dev/null @@ -1,81 +0,0 @@ -// Test the behavior of malloc/calloc/realloc when the allocation size is huge. -// By default (allocator_may_return_null=0) the process should crash. -// With allocator_may_return_null=1 the allocator should return 0. -// -// RUN: %clangxx_msan -O0 %s -o %t -// RUN: not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL - -#include <limits.h> -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <assert.h> -#include <limits> -int main(int argc, char **argv) { - volatile size_t size = std::numeric_limits<size_t>::max() - 10000; - assert(argc == 2); - char *x = 0; - if (!strcmp(argv[1], "malloc")) { - fprintf(stderr, "malloc:\n"); - x = (char*)malloc(size); - } - if (!strcmp(argv[1], "calloc")) { - fprintf(stderr, "calloc:\n"); - x = (char*)calloc(size / 4, 4); - } - - if (!strcmp(argv[1], "calloc-overflow")) { - fprintf(stderr, "calloc-overflow:\n"); - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - size_t kArraySize = 4096; - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - x = (char*)calloc(kArraySize, kArraySize2); - } - - if (!strcmp(argv[1], "realloc")) { - fprintf(stderr, "realloc:\n"); - x = (char*)realloc(0, size); - } - if (!strcmp(argv[1], "realloc-after-malloc")) { - fprintf(stderr, "realloc-after-malloc:\n"); - char *t = (char*)malloc(100); - *t = 42; - x = (char*)realloc(t, size); - assert(*t == 42); - } - // The NULL pointer is printed differently on different systems, while (long)0 - // is always the same. - fprintf(stderr, "x: %lx\n", (long)x); - return x != 0; -} -// CHECK-mCRASH: malloc: -// CHECK-mCRASH: MemorySanitizer's allocator is terminating the process -// CHECK-cCRASH: calloc: -// CHECK-cCRASH: MemorySanitizer's allocator is terminating the process -// CHECK-coCRASH: calloc-overflow: -// CHECK-coCRASH: MemorySanitizer's allocator is terminating the process -// CHECK-rCRASH: realloc: -// CHECK-rCRASH: MemorySanitizer's allocator is terminating the process -// CHECK-mrCRASH: realloc-after-malloc: -// CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process - -// CHECK-mNULL: malloc: -// CHECK-mNULL: x: 0 -// CHECK-cNULL: calloc: -// CHECK-cNULL: x: 0 -// CHECK-coNULL: calloc-overflow: -// CHECK-coNULL: x: 0 -// CHECK-rNULL: realloc: -// CHECK-rNULL: x: 0 -// CHECK-mrNULL: realloc-after-malloc: -// CHECK-mrNULL: x: 0 diff --git a/lib/msan/lit_tests/backtrace.cc b/lib/msan/lit_tests/backtrace.cc deleted file mode 100644 index 48684c29c60d6..0000000000000 --- a/lib/msan/lit_tests/backtrace.cc +++ /dev/null @@ -1,26 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <assert.h> -#include <execinfo.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -__attribute__((noinline)) -void f() { - void *buf[10]; - int sz = backtrace(buf, sizeof(buf) / sizeof(*buf)); - assert(sz > 0); - for (int i = 0; i < sz; ++i) - if (!buf[i]) - exit(1); - char **s = backtrace_symbols(buf, sz); - assert(s > 0); - for (int i = 0; i < sz; ++i) - printf("%d\n", strlen(s[i])); -} - -int main(void) { - f(); - return 0; -} diff --git a/lib/msan/lit_tests/c-strdup.c b/lib/msan/lit_tests/c-strdup.c deleted file mode 100644 index 7772f0f307b72..0000000000000 --- a/lib/msan/lit_tests/c-strdup.c +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clang_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 -// RUN: %clang_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 - -// Test that strdup in C programs is intercepted. -// GLibC headers translate strdup to __strdup at -O1 and higher. - -#include <stdlib.h> -#include <string.h> -int main(int argc, char **argv) { - char buf[] = "abc"; - char *p = strdup(buf); - if (*p) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/cxa_atexit.cc b/lib/msan/lit_tests/cxa_atexit.cc deleted file mode 100644 index f3641aadce03b..0000000000000 --- a/lib/msan/lit_tests/cxa_atexit.cc +++ /dev/null @@ -1,28 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p - -// PR17377: C++ module destructors get stale argument shadow. - -#include <stdio.h> -#include <stdlib.h> -class A { -public: - // This destructor get stale argument shadow left from the call to f(). - ~A() { - if (this) - exit(0); - } -}; - -A a; - -__attribute__((noinline)) -void f(long x) { -} - -int main(void) { - long x; - long * volatile p = &x; - // This call poisons TLS shadow for the first function argument. - f(*p); - return 0; -} diff --git a/lib/msan/lit_tests/default_blacklist.cc b/lib/msan/lit_tests/default_blacklist.cc deleted file mode 100644 index 32cc02257cb09..0000000000000 --- a/lib/msan/lit_tests/default_blacklist.cc +++ /dev/null @@ -1,3 +0,0 @@ -// Test that MSan uses the default blacklist from resource directory. -// RUN: %clangxx_msan -### %s 2>&1 | FileCheck %s -// CHECK: fsanitize-blacklist={{.*}}msan_blacklist.txt diff --git a/lib/msan/lit_tests/dlerror.cc b/lib/msan/lit_tests/dlerror.cc deleted file mode 100644 index 281b3164fd7e9..0000000000000 --- a/lib/msan/lit_tests/dlerror.cc +++ /dev/null @@ -1,14 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <assert.h> -#include <dlfcn.h> -#include <stdio.h> -#include <string.h> - -int main(void) { - void *p = dlopen("/bad/file/name", RTLD_NOW); - assert(!p); - char *s = dlerror(); - printf("%s, %zu\n", s, strlen(s)); - return 0; -} diff --git a/lib/msan/lit_tests/dso-origin.cc b/lib/msan/lit_tests/dso-origin.cc deleted file mode 100644 index 13661c65e744b..0000000000000 --- a/lib/msan/lit_tests/dso-origin.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Build a library with origin tracking and an executable w/o origin tracking. -// Test that origin tracking is enabled at runtime. -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %p/SharedLibs/dso-origin-so.cc \ -// RUN: -fPIC -shared -o %t-so.so -// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %t 2>&1 | FileCheck %s - -#include <stdlib.h> - -#include "SharedLibs/dso-origin.h" - -int main(int argc, char **argv) { - int *x = (int *)my_alloc(sizeof(int)); - my_access(x); - delete x; - - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in my_access .*dso-origin-so.cc:}} - // CHECK: {{#1 0x.* in main .*dso-origin.cc:}}[[@LINE-5]] - // CHECK: Uninitialized value was created by a heap allocation - // CHECK: {{#0 0x.* in .*malloc}} - // CHECK: {{#1 0x.* in my_alloc .*dso-origin-so.cc:}} - // CHECK: {{#2 0x.* in main .*dso-origin.cc:}}[[@LINE-10]] - // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*dso-origin-so.cc:.* my_access}} - return 0; -} diff --git a/lib/msan/lit_tests/errno.cc b/lib/msan/lit_tests/errno.cc deleted file mode 100644 index af27ad0b03298..0000000000000 --- a/lib/msan/lit_tests/errno.cc +++ /dev/null @@ -1,17 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <unistd.h> - -int main() -{ - int x; - int *volatile p = &x; - errno = *p; - int res = read(-1, 0, 0); - assert(res == -1); - if (errno) printf("errno %d\n", errno); - return 0; -} diff --git a/lib/msan/lit_tests/getaddrinfo-positive.cc b/lib/msan/lit_tests/getaddrinfo-positive.cc deleted file mode 100644 index 7fde1fdfab937..0000000000000 --- a/lib/msan/lit_tests/getaddrinfo-positive.cc +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <stdlib.h> - -volatile int z; - -int main(void) { - struct addrinfo *ai; - struct addrinfo hint; - int res = getaddrinfo("localhost", NULL, NULL, &ai); - if (ai) z = 1; // OK - res = getaddrinfo("localhost", NULL, &hint, &ai); - // CHECK: UMR in __interceptor_getaddrinfo at offset 0 inside - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: #0 {{.*}} in main {{.*}}getaddrinfo-positive.cc:[[@LINE-3]] - return 0; -} diff --git a/lib/msan/lit_tests/getaddrinfo.cc b/lib/msan/lit_tests/getaddrinfo.cc deleted file mode 100644 index 0518cf4733d0f..0000000000000 --- a/lib/msan/lit_tests/getaddrinfo.cc +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <sys/types.h> -#include <sys/socket.h> -#include <netdb.h> -#include <stdlib.h> - -void poison_stack_ahead() { - char buf[100000]; - // With -O0 this poisons a large chunk of stack. -} - -int main(void) { - poison_stack_ahead(); - - struct addrinfo *ai; - - // This should trigger loading of libnss_dns and friends. - // Those libraries are typically uninstrumented.They will call strlen() on a - // stack-allocated buffer, which is very likely to be poisoned. Test that we - // don't report this as an UMR. - int res = getaddrinfo("not-in-etc-hosts", NULL, NULL, &ai); - return 0; -} diff --git a/lib/msan/lit_tests/getline.cc b/lib/msan/lit_tests/getline.cc deleted file mode 100644 index 27168a8856066..0000000000000 --- a/lib/msan/lit_tests/getline.cc +++ /dev/null @@ -1,30 +0,0 @@ -// RUN: %clangxx_msan -O0 %s -o %t && %t %p - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main(int argc, char **argv) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "getline_test_data"); - - FILE *fp = fopen(buf, "r"); - assert(fp); - - char *line = 0; - size_t len = 0; - int n = getline(&line, &len, fp); - assert(n == 6); - assert(strcmp(line, "abcde\n") == 0); - - n = getline(&line, &len, fp); - assert(n == 6); - assert(strcmp(line, "12345\n") == 0); - - free(line); - fclose(fp); - - return 0; -} diff --git a/lib/msan/lit_tests/getline_test_data b/lib/msan/lit_tests/getline_test_data deleted file mode 100644 index 5ba1d4cec0dd2..0000000000000 --- a/lib/msan/lit_tests/getline_test_data +++ /dev/null @@ -1,2 +0,0 @@ -abcde -12345 diff --git a/lib/msan/lit_tests/heap-origin.cc b/lib/msan/lit_tests/heap-origin.cc deleted file mode 100644 index dfe7edd27e820..0000000000000 --- a/lib/msan/lit_tests/heap-origin.cc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out - -#include <stdlib.h> -int main(int argc, char **argv) { - char *volatile x = (char*)malloc(5 * sizeof(char)); - return *x; - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-2]] - - // CHECK-ORIGINS: Uninitialized value was created by a heap allocation - // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} - // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] - - // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} -} diff --git a/lib/msan/lit_tests/initgroups.cc b/lib/msan/lit_tests/initgroups.cc deleted file mode 100644 index adba5369579ac..0000000000000 --- a/lib/msan/lit_tests/initgroups.cc +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <sys/types.h> -#include <grp.h> - -int main(void) { - initgroups("root", 0); - // The above fails unless you are root. Does not matter, MSan false positive - // (which we are testing for) happens anyway. - return 0; -} diff --git a/lib/msan/lit_tests/inline.cc b/lib/msan/lit_tests/inline.cc deleted file mode 100644 index 4aeb15583f848..0000000000000 --- a/lib/msan/lit_tests/inline.cc +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %clangxx_msan -O3 %s -o %t && %t - -// Test that no_sanitize_memory attribute applies even when the function would -// be normally inlined. - -#include <stdlib.h> - -__attribute__((no_sanitize_memory)) -int f(int *p) { - if (*p) // BOOOM?? Nope! - exit(0); - return 0; -} - -int main(int argc, char **argv) { - int x; - int * volatile p = &x; - int res = f(p); - return 0; -} diff --git a/lib/msan/lit_tests/insertvalue_origin.cc b/lib/msan/lit_tests/insertvalue_origin.cc deleted file mode 100644 index 769ea45f8c4db..0000000000000 --- a/lib/msan/lit_tests/insertvalue_origin.cc +++ /dev/null @@ -1,35 +0,0 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out - -// Test origin propagation through insertvalue IR instruction. - -#include <stdio.h> -#include <stdint.h> - -struct mypair { - int64_t x; - int y; -}; - -mypair my_make_pair(int64_t x, int y) { - mypair p; - p.x = x; - p.y = y; - return p; -} - -int main() { - int64_t * volatile p = new int64_t; - mypair z = my_make_pair(*p, 0); - if (z.x) - printf("zzz\n"); - // CHECK: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-3]] - - // CHECK: Uninitialized value was created by a heap allocation - // CHECK: {{in main .*insertvalue_origin.cc:}}[[@LINE-8]] - delete p; - return 0; -} diff --git a/lib/msan/lit_tests/ioctl.cc b/lib/msan/lit_tests/ioctl.cc deleted file mode 100644 index caff80c2e5d74..0000000000000 --- a/lib/msan/lit_tests/ioctl.cc +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t -// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t - -#include <assert.h> -#include <stdlib.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <unistd.h> - -int main(int argc, char **argv) { - int fd = socket(AF_INET, SOCK_DGRAM, 0); - - unsigned int z; - int res = ioctl(fd, FIOGETOWN, &z); - assert(res == 0); - close(fd); - if (z) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/ioctl_custom.cc b/lib/msan/lit_tests/ioctl_custom.cc deleted file mode 100644 index 94ed528c70b91..0000000000000 --- a/lib/msan/lit_tests/ioctl_custom.cc +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t -// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t - -// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %t 2>&1 | FileCheck %s -// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %t 2>&1 | FileCheck %s - -#include <assert.h> -#include <stdlib.h> -#include <net/if.h> -#include <stdio.h> -#include <string.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <unistd.h> - -int main(int argc, char **argv) { - int fd = socket(AF_INET, SOCK_STREAM, 0); - - struct ifreq ifreqs[20]; - struct ifconf ifc; - ifc.ifc_ifcu.ifcu_req = ifreqs; -#ifndef POSITIVE - ifc.ifc_len = sizeof(ifreqs); -#endif - int res = ioctl(fd, SIOCGIFCONF, (void *)&ifc); - // CHECK: UMR in ioctl{{.*}} at offset 0 - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: #{{.*}} in main {{.*}}ioctl_custom.cc:[[@LINE-3]] - assert(res == 0); - for (int i = 0; i < ifc.ifc_len / sizeof(*ifc.ifc_ifcu.ifcu_req); ++i) - printf("%d %zu %s\n", i, strlen(ifreqs[i].ifr_name), ifreqs[i].ifr_name); - return 0; -} diff --git a/lib/msan/lit_tests/keep-going-dso.cc b/lib/msan/lit_tests/keep-going-dso.cc deleted file mode 100644 index 6d006756a110f..0000000000000 --- a/lib/msan/lit_tests/keep-going-dso.cc +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out - -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out - -// Test how -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going affect reports -// from interceptors. -// -mllvm -msan-keep-going provides the default value of keep_going flag, but is -// always overwritten by MSAN_OPTIONS - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main(int argc, char **argv) { - char *volatile x = (char*)malloc(5 * sizeof(char)); - x[4] = 0; - if (strlen(x) < 3) - exit(0); - fprintf(stderr, "Done\n"); - // CHECK-NOT: Done - // CHECK-KEEP-GOING: Done - return 0; -} diff --git a/lib/msan/lit_tests/keep-going.cc b/lib/msan/lit_tests/keep-going.cc deleted file mode 100644 index e33b137c76f78..0000000000000 --- a/lib/msan/lit_tests/keep-going.cc +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 -// FileCheck %s <%t.out - -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=1 not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && MSAN_OPTIONS=halt_on_error=0 not %t >%t.out 2>&1 -// FileCheck --check-prefix=CHECK-KEEP-GOING %s <%t.out - -// Test behaviour of -mllvm -msan-keep-going and MSAN_OPTIONS=keep_going. -// -mllvm -msan-keep-going provides the default value of keep_going flag; value -// of 1 can be overwritten by MSAN_OPTIONS, value of 0 can not. - -#include <stdio.h> -#include <stdlib.h> - -int main(int argc, char **argv) { - char *volatile x = (char*)malloc(5 * sizeof(char)); - if (x[0]) - exit(0); - fprintf(stderr, "Done\n"); - // CHECK-NOT: Done - // CHECK-KEEP-GOING: Done - return 0; -} diff --git a/lib/msan/lit_tests/lit.cfg b/lib/msan/lit_tests/lit.cfg deleted file mode 100644 index da1bde6dd04ad..0000000000000 --- a/lib/msan/lit_tests/lit.cfg +++ /dev/null @@ -1,74 +0,0 @@ -# -*- Python -*- - -import os - -import lit.util - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit_config.fatal( - "No attribute %r in test configuration! You may need to run " - "tests from your build directory or add this attribute " - "to lit.site.cfg " % attr_name) - return attr_value - -# Setup config name. -config.name = 'MemorySanitizer' - -# Setup source root. -config.test_source_root = os.path.dirname(__file__) - -def DisplayNoConfigMessage(): - lit_config.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-msan") - -# Figure out LLVM source root. -llvm_src_root = getattr(config, 'llvm_src_root', None) -if llvm_src_root is None: - # We probably haven't loaded the site-specific configuration: the user - # is likely trying to run a test file directly, and the site configuration - # wasn't created by the build system. - msan_site_cfg = lit_config.params.get('msan_site_config', None) - if (msan_site_cfg) and (os.path.exists(msan_site_cfg)): - lit_config.load_config(config, msan_site_cfg) - raise SystemExit - - # Try to guess the location of site-specific configuration using llvm-config - # util that can point where the build tree is. - llvm_config = lit.util.which("llvm-config", config.environment["PATH"]) - if not llvm_config: - DisplayNoConfigMessage() - - # Find out the presumed location of generated site config. - llvm_obj_root = lit.util.capture(["llvm-config", "--obj-root"]).strip() - msan_site_cfg = os.path.join(llvm_obj_root, "projects", "compiler-rt", - "lib", "msan", "lit_tests", "lit.site.cfg") - if (not msan_site_cfg) or (not os.path.exists(msan_site_cfg)): - DisplayNoConfigMessage() - - lit_config.load_config(config, msan_site_cfg) - raise SystemExit - -# Setup default compiler flags used with -fsanitize=memory option. -clang_msan_cflags = ["-fsanitize=memory", - "-mno-omit-leaf-frame-pointer", - "-fno-omit-frame-pointer", - "-fno-optimize-sibling-calls", - "-g", - "-m64"] -clang_msan_cxxflags = ["--driver-mode=g++ "] + clang_msan_cflags -config.substitutions.append( ("%clang_msan ", - " ".join([config.clang] + clang_msan_cflags) + - " ") ) -config.substitutions.append( ("%clangxx_msan ", - " ".join([config.clang] + clang_msan_cxxflags) + - " ") ) - -# Default test suffixes. -config.suffixes = ['.c', '.cc', '.cpp'] - -# MemorySanitizer tests are currently supported on Linux only. -if config.host_os not in ['Linux']: - config.unsupported = True diff --git a/lib/msan/lit_tests/lit.site.cfg.in b/lib/msan/lit_tests/lit.site.cfg.in deleted file mode 100644 index 946df778f3d39..0000000000000 --- a/lib/msan/lit_tests/lit.site.cfg.in +++ /dev/null @@ -1,5 +0,0 @@ -# Load common config for all compiler-rt lit tests. -lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") - -# Load tool-specific config that would do the real work. -lit_config.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/lit.cfg") diff --git a/lib/msan/lit_tests/malloc_hook.cc b/lib/msan/lit_tests/malloc_hook.cc deleted file mode 100644 index fc68fbc35fbb0..0000000000000 --- a/lib/msan/lit_tests/malloc_hook.cc +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: %clangxx_msan -O2 %s -o %t -// RUN: %t 2>&1 | FileCheck %s -#include <stdlib.h> -#include <unistd.h> - -extern "C" { -int __msan_get_ownership(const void *p); - -void *global_ptr; - -// Note: avoid calling functions that allocate memory in malloc/free -// to avoid infinite recursion. -void __msan_malloc_hook(void *ptr, size_t sz) { - if (__msan_get_ownership(ptr)) { - write(1, "MallocHook\n", sizeof("MallocHook\n")); - global_ptr = ptr; - } -} -void __msan_free_hook(void *ptr) { - if (__msan_get_ownership(ptr) && ptr == global_ptr) - write(1, "FreeHook\n", sizeof("FreeHook\n")); -} -} // extern "C" - -int main() { - volatile int *x = new int; - // CHECK: MallocHook - // Check that malloc hook was called with correct argument. - if (global_ptr != (void*)x) { - _exit(1); - } - *x = 0; - delete x; - // CHECK: FreeHook - return 0; -} diff --git a/lib/msan/lit_tests/no_sanitize_memory.cc b/lib/msan/lit_tests/no_sanitize_memory.cc deleted file mode 100644 index 48afc17e35e92..0000000000000 --- a/lib/msan/lit_tests/no_sanitize_memory.cc +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 - -// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 - -// Test that (no_sanitize_memory) functions -// * don't check shadow values (-DCHECK_IN_F) -// * treat all values loaded from memory as fully initialized (-UCHECK_IN_F) - -#include <stdlib.h> -#include <stdio.h> - -__attribute__((noinline)) -__attribute__((no_sanitize_memory)) -int f(void) { - int x; - int * volatile p = &x; -#ifdef CHECK_IN_F - if (*p) - exit(0); -#endif - return *p; -} - -int main(void) { - if (f()) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/no_sanitize_memory_prop.cc b/lib/msan/lit_tests/no_sanitize_memory_prop.cc deleted file mode 100644 index 3551524788523..0000000000000 --- a/lib/msan/lit_tests/no_sanitize_memory_prop.cc +++ /dev/null @@ -1,33 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -// Test that (no_sanitize_memory) functions propagate shadow. - -// Note that at -O0 there is no report, because 'x' in 'f' is spilled to the -// stack, and then loaded back as a fully initialiazed value (due to -// no_sanitize_memory attribute). - -#include <stdlib.h> -#include <stdio.h> - -__attribute__((noinline)) -__attribute__((no_sanitize_memory)) -int f(int x) { - return x; -} - -int main(void) { - int x; - int * volatile p = &x; - int y = f(*p); - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*no_sanitize_memory_prop.cc:}}[[@LINE+1]] - if (y) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/poison_in_free.cc b/lib/msan/lit_tests/poison_in_free.cc deleted file mode 100644 index f134d05abb1ee..0000000000000 --- a/lib/msan/lit_tests/poison_in_free.cc +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clangxx_msan -O0 %s -o %t && not %t >%t.out 2>&1 -// FileCheck %s <%t.out -// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=poison_in_free=0 %t >%t.out 2>&1 - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -int main(int argc, char **argv) { - char *volatile x = (char*)malloc(50 * sizeof(char)); - memset(x, 0, 50); - free(x); - return x[25]; - // CHECK: MemorySanitizer: use-of-uninitialized-value - // CHECK: #0 {{.*}} in main{{.*}}poison_in_free.cc:[[@LINE-2]] -} diff --git a/lib/msan/lit_tests/ptrace.cc b/lib/msan/lit_tests/ptrace.cc deleted file mode 100644 index d0e83eabd6a4e..0000000000000 --- a/lib/msan/lit_tests/ptrace.cc +++ /dev/null @@ -1,36 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <assert.h> -#include <stdio.h> -#include <sys/ptrace.h> -#include <sys/types.h> -#include <sys/user.h> -#include <sys/wait.h> -#include <unistd.h> - -int main(void) { - pid_t pid; - pid = fork(); - if (pid == 0) { // child - ptrace(PTRACE_TRACEME, 0, NULL, NULL); - execl("/bin/true", "true", NULL); - } else { - wait(NULL); - user_regs_struct regs; - int res; - res = ptrace(PTRACE_GETREGS, pid, NULL, ®s); - assert(!res); - if (regs.rip) - printf("%zx\n", regs.rip); - - user_fpregs_struct fpregs; - res = ptrace(PTRACE_GETFPREGS, pid, NULL, &fpregs); - assert(!res); - if (fpregs.mxcsr) - printf("%x\n", fpregs.mxcsr); - - ptrace(PTRACE_CONT, pid, NULL, NULL); - wait(NULL); - } - return 0; -} diff --git a/lib/msan/lit_tests/readdir64.cc b/lib/msan/lit_tests/readdir64.cc deleted file mode 100644 index 0ec106c741f51..0000000000000 --- a/lib/msan/lit_tests/readdir64.cc +++ /dev/null @@ -1,27 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t - -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %t -// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %t - -// Test that readdir64 is intercepted as well as readdir. - -#include <sys/types.h> -#include <dirent.h> -#include <stdlib.h> - - -int main(void) { - DIR *dir = opendir("."); - struct dirent *d = readdir(dir); - if (d->d_name[0]) { - closedir(dir); - exit(0); - } - closedir(dir); - return 0; -} diff --git a/lib/msan/lit_tests/scandir.cc b/lib/msan/lit_tests/scandir.cc deleted file mode 100644 index 94672e1adbeef..0000000000000 --- a/lib/msan/lit_tests/scandir.cc +++ /dev/null @@ -1,56 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <sys/stat.h> -#include <sys/types.h> -#include <dirent.h> -#include <unistd.h> - -#include <sanitizer/msan_interface.h> - - -static int my_filter(const struct dirent *a) { - assert(__msan_test_shadow(&a, sizeof(a)) == (size_t)-1); - printf("%s\n", a->d_name); - __msan_print_shadow(a, a->d_reclen); - assert(__msan_test_shadow(a, a->d_reclen) == (size_t)-1); - printf("%s\n", a->d_name); - return strlen(a->d_name) == 3 && a->d_name[2] == 'b'; -} - -static int my_compar(const struct dirent **a, const struct dirent **b) { - assert(__msan_test_shadow(a, sizeof(*a)) == (size_t)-1); - assert(__msan_test_shadow(*a, (*a)->d_reclen) == (size_t)-1); - assert(__msan_test_shadow(b, sizeof(*b)) == (size_t)-1); - assert(__msan_test_shadow(*b, (*b)->d_reclen) == (size_t)-1); - if ((*a)->d_name[1] == (*b)->d_name[1]) - return 0; - return ((*a)->d_name[1] < (*b)->d_name[1]) ? 1 : -1; -} - -int main(int argc, char *argv[]) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); - - struct dirent **d; - int res = scandir(buf, &d, my_filter, my_compar); - assert(res == 2); - assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); - for (int i = 0; i < res; ++i) { - assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); - assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); - } - - assert(strcmp(d[0]->d_name, "bbb") == 0); - assert(strcmp(d[1]->d_name, "aab") == 0); - return 0; -} diff --git a/lib/msan/lit_tests/scandir_null.cc b/lib/msan/lit_tests/scandir_null.cc deleted file mode 100644 index 84af7f418d219..0000000000000 --- a/lib/msan/lit_tests/scandir_null.cc +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p -// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p -// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p - -#include <assert.h> -#include <glob.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <sys/stat.h> -#include <sys/types.h> -#include <dirent.h> -#include <unistd.h> - -#include <sanitizer/msan_interface.h> - - -int main(int argc, char *argv[]) { - assert(argc == 2); - char buf[1024]; - snprintf(buf, sizeof(buf), "%s/%s", argv[1], "scandir_test_root/"); - - struct dirent **d; - int res = scandir(buf, &d, NULL, NULL); - assert(res >= 3); - assert(__msan_test_shadow(&d, sizeof(*d)) == (size_t)-1); - for (int i = 0; i < res; ++i) { - assert(__msan_test_shadow(&d[i], sizeof(d[i])) == (size_t)-1); - assert(__msan_test_shadow(d[i], d[i]->d_reclen) == (size_t)-1); - } - return 0; -} diff --git a/lib/msan/lit_tests/scandir_test_root/aaa b/lib/msan/lit_tests/scandir_test_root/aaa deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/scandir_test_root/aaa +++ /dev/null diff --git a/lib/msan/lit_tests/scandir_test_root/aab b/lib/msan/lit_tests/scandir_test_root/aab deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/scandir_test_root/aab +++ /dev/null diff --git a/lib/msan/lit_tests/scandir_test_root/bbb b/lib/msan/lit_tests/scandir_test_root/bbb deleted file mode 100644 index e69de29bb2d1d..0000000000000 --- a/lib/msan/lit_tests/scandir_test_root/bbb +++ /dev/null diff --git a/lib/msan/lit_tests/select.cc b/lib/msan/lit_tests/select.cc deleted file mode 100644 index a169a2dd9118c..0000000000000 --- a/lib/msan/lit_tests/select.cc +++ /dev/null @@ -1,22 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -#include <stdlib.h> -int main(int argc, char **argv) { - int x; - int *volatile p = &x; - int z = *p ? 1 : 0; - if (z) - exit(0); - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*select.cc:}}[[@LINE-3]] - - // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*select.cc:.* main}} - return 0; -} diff --git a/lib/msan/lit_tests/setlocale.cc b/lib/msan/lit_tests/setlocale.cc deleted file mode 100644 index a22b744d74db7..0000000000000 --- a/lib/msan/lit_tests/setlocale.cc +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <assert.h> -#include <locale.h> -#include <stdlib.h> - -int main(void) { - char *locale = setlocale (LC_ALL, ""); - assert(locale); - if (locale[0]) - exit(0); - return 0; -} diff --git a/lib/msan/lit_tests/signal_stress_test.cc b/lib/msan/lit_tests/signal_stress_test.cc deleted file mode 100644 index ea75eae1bdaaf..0000000000000 --- a/lib/msan/lit_tests/signal_stress_test.cc +++ /dev/null @@ -1,71 +0,0 @@ -// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %t - -// Test that va_arg shadow from a signal handler does not leak outside. - -#include <signal.h> -#include <stdarg.h> -#include <sanitizer/msan_interface.h> -#include <assert.h> -#include <sys/time.h> -#include <stdio.h> - -const int kSigCnt = 200; - -void f(bool poisoned, int n, ...) { - va_list vl; - va_start(vl, n); - for (int i = 0; i < n; ++i) { - void *p = va_arg(vl, void *); - if (!poisoned) - assert(__msan_test_shadow(&p, sizeof(p)) == -1); - } - va_end(vl); -} - -int sigcnt; - -void SignalHandler(int signo) { - assert(signo == SIGPROF); - void *p; - void **volatile q = &p; - f(true, 10, - *q, *q, *q, *q, *q, - *q, *q, *q, *q, *q); - ++sigcnt; -} - -int main() { - signal(SIGPROF, SignalHandler); - - itimerval itv; - itv.it_interval.tv_sec = 0; - itv.it_interval.tv_usec = 100; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 100; - setitimer(ITIMER_PROF, &itv, NULL); - - void *p; - void **volatile q = &p; - - do { - f(false, 20, - nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr); - f(true, 20, - *q, *q, *q, *q, *q, - *q, *q, *q, *q, *q, - *q, *q, *q, *q, *q, - *q, *q, *q, *q, *q); - } while (sigcnt < kSigCnt); - - itv.it_interval.tv_sec = 0; - itv.it_interval.tv_usec = 0; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 0; - setitimer(ITIMER_PROF, &itv, NULL); - - signal(SIGPROF, SIG_DFL); - return 0; -} diff --git a/lib/msan/lit_tests/sigwait.cc b/lib/msan/lit_tests/sigwait.cc deleted file mode 100644 index 29aa86c938f2a..0000000000000 --- a/lib/msan/lit_tests/sigwait.cc +++ /dev/null @@ -1,30 +0,0 @@ -// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t - -#include <assert.h> -#include <sanitizer/msan_interface.h> -#include <signal.h> -#include <sys/time.h> -#include <unistd.h> - -void test_sigwait() { - sigset_t s; - sigemptyset(&s); - sigaddset(&s, SIGUSR1); - sigprocmask(SIG_BLOCK, &s, 0); - - if (pid_t pid = fork()) { - kill(pid, SIGUSR1); - _exit(0); - } else { - int sig; - int res = sigwait(&s, &sig); - assert(!res); - // The following checks that sig is initialized. - assert(sig == SIGUSR1); - } -} - -int main(void) { - test_sigwait(); - return 0; -} diff --git a/lib/msan/lit_tests/sigwaitinfo.cc b/lib/msan/lit_tests/sigwaitinfo.cc deleted file mode 100644 index d4f004598a62f..0000000000000 --- a/lib/msan/lit_tests/sigwaitinfo.cc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %t - -#include <assert.h> -#include <sanitizer/msan_interface.h> -#include <signal.h> -#include <sys/time.h> -#include <unistd.h> - -void test_sigwaitinfo() { - sigset_t s; - sigemptyset(&s); - sigaddset(&s, SIGUSR1); - sigprocmask(SIG_BLOCK, &s, 0); - - if (pid_t pid = fork()) { - kill(pid, SIGUSR1); - _exit(0); - } else { - siginfo_t info; - int res = sigwaitinfo(&s, &info); - assert(!res); - // The following checks that sig is initialized. - assert(info.si_signo == SIGUSR1); - assert(-1 == __msan_test_shadow(&info, sizeof(info))); - } -} - -int main(void) { - test_sigwaitinfo(); - return 0; -} diff --git a/lib/msan/lit_tests/stack-origin.cc b/lib/msan/lit_tests/stack-origin.cc deleted file mode 100644 index b0b05d9658bfb..0000000000000 --- a/lib/msan/lit_tests/stack-origin.cc +++ /dev/null @@ -1,31 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out - -#include <stdlib.h> -int main(int argc, char **argv) { - int x; - int *volatile p = &x; - return *p; - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-2]] - - // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' - // CHECK-ORIGINS: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-8]] - - // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin.cc:.* main}} -} diff --git a/lib/msan/lit_tests/sync_lock_set_and_test.cc b/lib/msan/lit_tests/sync_lock_set_and_test.cc deleted file mode 100644 index 1023b3e543681..0000000000000 --- a/lib/msan/lit_tests/sync_lock_set_and_test.cc +++ /dev/null @@ -1,7 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -int main(void) { - int i; - __sync_lock_test_and_set(&i, 0); - return i; -} diff --git a/lib/msan/lit_tests/tzset.cc b/lib/msan/lit_tests/tzset.cc deleted file mode 100644 index 7e1c2cfad5666..0000000000000 --- a/lib/msan/lit_tests/tzset.cc +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t - -#include <stdlib.h> -#include <string.h> -#include <time.h> - -extern char *tzname[2]; - -int main(void) { - if (!strlen(tzname[0]) || !strlen(tzname[1])) - exit(1); - tzset(); - if (!strlen(tzname[0]) || !strlen(tzname[1])) - exit(1); - return 0; -} diff --git a/lib/msan/lit_tests/unaligned_read_origin.cc b/lib/msan/lit_tests/unaligned_read_origin.cc deleted file mode 100644 index fa29ab69de1b0..0000000000000 --- a/lib/msan/lit_tests/unaligned_read_origin.cc +++ /dev/null @@ -1,16 +0,0 @@ -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out - -#include <sanitizer/msan_interface.h> - -int main(int argc, char **argv) { - int x; - int *volatile p = &x; - return __sanitizer_unaligned_load32(p); - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-2]] - // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' - // CHECK: {{#0 0x.* in main .*unaligned_read_origin.cc:}}[[@LINE-7]] -} diff --git a/lib/msan/lit_tests/use-after-free.cc b/lib/msan/lit_tests/use-after-free.cc deleted file mode 100644 index ac47c0233a10f..0000000000000 --- a/lib/msan/lit_tests/use-after-free.cc +++ /dev/null @@ -1,34 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out -// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out - -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out -// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 -// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out - -#include <stdlib.h> -int main(int argc, char **argv) { - int *volatile p = (int *)malloc(sizeof(int)); - *p = 42; - free(p); - - if (*p) - exit(0); - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: {{#0 0x.* in main .*use-after-free.cc:}}[[@LINE-3]] - - // CHECK-ORIGINS: Uninitialized value was created by a heap allocation - // CHECK-ORIGINS: {{#0 0x.* in .*free}} - // CHECK-ORIGINS: {{#1 0x.* in main .*use-after-free.cc:}}[[@LINE-9]] - return 0; -} diff --git a/lib/msan/lit_tests/vector_cvt.cc b/lib/msan/lit_tests/vector_cvt.cc deleted file mode 100644 index c200c77de96a7..0000000000000 --- a/lib/msan/lit_tests/vector_cvt.cc +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t -// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %t 2>&1 | FileCheck %s - -#include <emmintrin.h> - -int to_int(double v) { - __m128d t = _mm_set_sd(v); - int x = _mm_cvtsd_si32(t); - return x; - // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value - // CHECK: #{{.*}} in to_int{{.*}}vector_cvt.cc:[[@LINE-4]] -} - -int main() { -#ifdef POSITIVE - double v; -#else - double v = 1.1; -#endif - double* volatile p = &v; - int x = to_int(*p); - return !x; -} diff --git a/lib/msan/lit_tests/vector_select.cc b/lib/msan/lit_tests/vector_select.cc deleted file mode 100644 index e8d55423293c4..0000000000000 --- a/lib/msan/lit_tests/vector_select.cc +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %clangxx_msan -m64 -O0 %s -c -o %t -// RUN: %clangxx_msan -m64 -O3 %s -c -o %t - -// Regression test for MemorySanitizer instrumentation of a select instruction -// with vector arguments. - -#include <emmintrin.h> - -__m128d select(bool b, __m128d c, __m128d d) -{ - return b ? c : d; -} - diff --git a/lib/msan/lit_tests/wrap_indirect_calls.cc b/lib/msan/lit_tests/wrap_indirect_calls.cc deleted file mode 100644 index b4bac1ecbd222..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Test indirect call wrapping in MemorySanitizer. - -// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/two.cc -fPIC -shared -o %t-two-so.so -// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/wrapper.cc -fPIC -shared -o %t-wrapper-so.so - -// Disable fast path. - -// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ -// RUN: %t-two-so.so %t-wrapper-so.so \ -// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ -// RUN: -mllvm -msan-wrap-indirect-calls-fast=0 \ -// RUN: -DSLOW=1 \ -// RUN: -Wl,--defsym=__executable_start=0 -o %t -// RUN: %t - -// Enable fast path, call from executable, -O0. - -// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ -// RUN: %t-two-so.so %t-wrapper-so.so \ -// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ -// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ -// RUN: -DSLOW=0 \ -// RUN: -Wl,--defsym=__executable_start=0 -o %t -// RUN: %t - -// Enable fast path, call from executable, -O3. - -// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc %s \ -// RUN: %t-two-so.so %t-wrapper-so.so \ -// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ -// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ -// RUN: -DSLOW=0 \ -// RUN: -Wl,--defsym=__executable_start=0 -o %t -// RUN: %t - -// Enable fast path, call from DSO, -O0. - -// RUN: %clangxx_msan -O0 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ -// RUN: %t-two-so.so %t-wrapper-so.so \ -// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ -// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ -// RUN: -DSLOW=0 \ -// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so -// RUN: %clangxx_msan -O0 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t -// RUN: %t - -// Enable fast path, call from DSO, -O3. - -// RUN: %clangxx_msan -O3 %p/wrap_indirect_calls/caller.cc %p/wrap_indirect_calls/one.cc -shared \ -// RUN: %t-two-so.so %t-wrapper-so.so \ -// RUN: -mllvm -msan-wrap-indirect-calls=wrapper \ -// RUN: -mllvm -msan-wrap-indirect-calls-fast=1 \ -// RUN: -DSLOW=0 \ -// RUN: -Wl,--defsym=__executable_start=0 -o %t-caller-so.so -// RUN: %clangxx_msan -O3 %s %t-caller-so.so %t-two-so.so %t-wrapper-so.so -o %t -// RUN: %t - -// The actual test is in multiple files in wrap_indirect_calls/ directory. -void run_test(); - -int main() { - run_test(); - return 0; -} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/caller.cc b/lib/msan/lit_tests/wrap_indirect_calls/caller.cc deleted file mode 100644 index a0af8b7bb0c53..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls/caller.cc +++ /dev/null @@ -1,51 +0,0 @@ -// Indirectly call a bunch of functions. - -#include <assert.h> - -extern int cnt; - -typedef int (*F)(int, int); - -// A function in the same object. -int f_local(int x, int y) { - return x + y; -} - -// A function in another object. -int f_other_object(int x, int y); - -// A function in another DSO. -int f_dso(int x, int y); - -// A function in another DSO that is replaced by the wrapper. -int f_replaced(int x, int y); - -void run_test(void) { - int x; - int expected_cnt = 0; - volatile F f; - - if (SLOW) ++expected_cnt; - f = &f_local; - x = f(1, 2); - assert(x == 3); - assert(cnt == expected_cnt); - - if (SLOW) ++expected_cnt; - f = &f_other_object; - x = f(2, 3); - assert(x == 6); - assert(cnt == expected_cnt); - - ++expected_cnt; - f = &f_dso; - x = f(2, 3); - assert(x == 7); - assert(cnt == expected_cnt); - - ++expected_cnt; - f = &f_replaced; - x = f(2, 3); - assert(x == 11); - assert(cnt == expected_cnt); -} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg b/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg deleted file mode 100644 index 5e01230c09864..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg +++ /dev/null @@ -1,3 +0,0 @@ -# Sources in this directory are used by tests in parent directory. - -config.suffixes = [] diff --git a/lib/msan/lit_tests/wrap_indirect_calls/one.cc b/lib/msan/lit_tests/wrap_indirect_calls/one.cc deleted file mode 100644 index ab7bf4125c0a3..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls/one.cc +++ /dev/null @@ -1,3 +0,0 @@ -int f_other_object(int x, int y) { - return x * y; -} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/two.cc b/lib/msan/lit_tests/wrap_indirect_calls/two.cc deleted file mode 100644 index c939a993bc9a0..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls/two.cc +++ /dev/null @@ -1,11 +0,0 @@ -int f_dso(int x, int y) { - return 2 * x + y; -} - -int f_replaced(int x, int y) { - return x + y + 5; -} - -int f_replacement(int x, int y) { - return x + y + 6; -} diff --git a/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc b/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc deleted file mode 100644 index 8fcd0c635d964..0000000000000 --- a/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc +++ /dev/null @@ -1,11 +0,0 @@ -int f_replaced(int x, int y); -int f_replacement(int x, int y); - -int cnt; - -extern "C" void *wrapper(void *p) { - ++cnt; - if (p == (void *)f_replaced) - return (void *)f_replacement; - return p; -} diff --git a/lib/msan/msan.cc b/lib/msan/msan.cc index 83b11e5c2ff31..853e448fab8ea 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -13,6 +13,9 @@ //===----------------------------------------------------------------------===// #include "msan.h" +#include "msan_chained_origin_depot.h" +#include "msan_origin.h" +#include "msan_thread.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" @@ -20,8 +23,8 @@ #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "sanitizer_common/sanitizer_symbolizer.h" +#include "sanitizer_common/sanitizer_stackdepot.h" -#include "interception/interception.h" // ACHTUNG! No system header includes in this file. @@ -31,22 +34,25 @@ using namespace __sanitizer; static THREADLOCAL int msan_expect_umr = 0; static THREADLOCAL int msan_expected_umr_found = 0; -static int msan_running_under_dr = 0; - +// Function argument shadow. Each argument starts at the next available 8-byte +// aligned address. SANITIZER_INTERFACE_ATTRIBUTE -THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSizeInWords]; +THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSize / sizeof(u64)]; +// Function argument origin. Each argument starts at the same offset as the +// corresponding shadow in (__msan_param_tls). Slightly weird, but changing this +// would break compatibility with older prebuilt binaries. SANITIZER_INTERFACE_ATTRIBUTE -THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSizeInWords]; +THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSize / sizeof(u32)]; SANITIZER_INTERFACE_ATTRIBUTE -THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSizeInWords]; +THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSize / sizeof(u64)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_retval_origin_tls; SANITIZER_INTERFACE_ATTRIBUTE -THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSizeInWords]; +THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)]; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u64 __msan_va_arg_overflow_size_tls; @@ -54,12 +60,7 @@ THREADLOCAL u64 __msan_va_arg_overflow_size_tls; SANITIZER_INTERFACE_ATTRIBUTE THREADLOCAL u32 __msan_origin_tls; -static THREADLOCAL struct { - uptr stack_top, stack_bottom; -} __msan_stack_bounds; - static THREADLOCAL int is_in_symbolizer; -static THREADLOCAL int is_in_loader; extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins; @@ -71,34 +72,10 @@ extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_keep_going; namespace __msan { -static bool IsRunningUnderDr() { - bool result = false; - MemoryMappingLayout proc_maps(/*cache_enabled*/true); - const sptr kBufSize = 4095; - char *filename = (char*)MmapOrDie(kBufSize, __FUNCTION__); - while (proc_maps.Next(/* start */0, /* end */0, /* file_offset */0, - filename, kBufSize, /* protection */0)) { - if (internal_strstr(filename, "libdynamorio") != 0) { - result = true; - break; - } - } - UnmapOrDie(filename, kBufSize); - return result; -} - void EnterSymbolizer() { ++is_in_symbolizer; } void ExitSymbolizer() { --is_in_symbolizer; } bool IsInSymbolizer() { return is_in_symbolizer; } -void EnterLoader() { ++is_in_loader; } -void ExitLoader() { --is_in_loader; } - -extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE -bool __msan_is_in_loader() { return is_in_loader; } -} - static Flags msan_flags; Flags *flags() { @@ -110,6 +87,8 @@ bool msan_init_is_running; int msan_report_count = 0; +void (*death_callback)(void); + // Array of stack origins. // FIXME: make it resizable. static const uptr kNumStackOriginDescrs = 1024 * 1024; @@ -118,33 +97,63 @@ static uptr StackOriginPC[kNumStackOriginDescrs]; static atomic_uint32_t NumStackOriginDescrs; static void ParseFlagsFromString(Flags *f, const char *str) { - ParseCommonFlagsFromString(str); - ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes"); - ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes"); - ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc"); - ParseFlag(str, &f->poison_in_free, "poison_in_free"); - ParseFlag(str, &f->exit_code, "exit_code"); + CommonFlags *cf = common_flags(); + ParseCommonFlagsFromString(cf, str); + ParseFlag(str, &f->poison_heap_with_zeroes, "poison_heap_with_zeroes", ""); + ParseFlag(str, &f->poison_stack_with_zeroes, "poison_stack_with_zeroes", ""); + ParseFlag(str, &f->poison_in_malloc, "poison_in_malloc", ""); + ParseFlag(str, &f->poison_in_free, "poison_in_free", ""); + ParseFlag(str, &f->exit_code, "exit_code", ""); if (f->exit_code < 0 || f->exit_code > 127) { Printf("Exit code not in [0, 128) range: %d\n", f->exit_code); Die(); } - ParseFlag(str, &f->report_umrs, "report_umrs"); - ParseFlag(str, &f->wrap_signals, "wrap_signals"); + ParseFlag(str, &f->origin_history_size, "origin_history_size", ""); + if (f->origin_history_size < 0 || + f->origin_history_size > Origin::kMaxDepth) { + Printf( + "Origin history size invalid: %d. Must be 0 (unlimited) or in [1, %d] " + "range.\n", + f->origin_history_size, Origin::kMaxDepth); + Die(); + } + ParseFlag(str, &f->origin_history_per_stack_limit, + "origin_history_per_stack_limit", ""); + // Limiting to kStackDepotMaxUseCount / 2 to avoid overflow in + // StackDepotHandle::inc_use_count_unsafe. + if (f->origin_history_per_stack_limit < 0 || + f->origin_history_per_stack_limit > kStackDepotMaxUseCount / 2) { + Printf( + "Origin per-stack limit invalid: %d. Must be 0 (unlimited) or in [1, " + "%d] range.\n", + f->origin_history_per_stack_limit, kStackDepotMaxUseCount / 2); + Die(); + } + + ParseFlag(str, &f->report_umrs, "report_umrs", ""); + ParseFlag(str, &f->wrap_signals, "wrap_signals", ""); + ParseFlag(str, &f->print_stats, "print_stats", ""); + ParseFlag(str, &f->atexit, "atexit", ""); + ParseFlag(str, &f->store_context_size, "store_context_size", ""); + if (f->store_context_size < 1) f->store_context_size = 1; // keep_going is an old name for halt_on_error, // and it has inverse meaning. f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "keep_going"); + ParseFlag(str, &f->halt_on_error, "keep_going", ""); f->halt_on_error = !f->halt_on_error; - ParseFlag(str, &f->halt_on_error, "halt_on_error"); + ParseFlag(str, &f->halt_on_error, "halt_on_error", ""); } static void InitializeFlags(Flags *f, const char *options) { CommonFlags *cf = common_flags(); - SetCommonFlagDefaults(); + SetCommonFlagsDefaults(cf); cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); cf->malloc_context_size = 20; cf->handle_ioctl = true; + // FIXME: test and enable. + cf->check_printf = false; + cf->intercept_tls_get_addr = true; internal_memset(f, 0, sizeof(*f)); f->poison_heap_with_zeroes = false; @@ -152,9 +161,14 @@ static void InitializeFlags(Flags *f, const char *options) { f->poison_in_malloc = true; f->poison_in_free = true; f->exit_code = 77; + f->origin_history_size = Origin::kMaxDepth; + f->origin_history_per_stack_limit = 20000; f->report_umrs = true; f->wrap_signals = true; + f->print_stats = false; + f->atexit = false; f->halt_on_error = !&__msan_keep_going; + f->store_context_size = 20; // Override from user-specified string. if (__msan_default_options) @@ -162,39 +176,22 @@ static void InitializeFlags(Flags *f, const char *options) { ParseFlagsFromString(f, options); } -static void GetCurrentStackBounds(uptr *stack_top, uptr *stack_bottom) { - if (__msan_stack_bounds.stack_top == 0) { - // Break recursion (GetStackTrace -> GetThreadStackTopAndBottom -> - // realloc -> GetStackTrace). - __msan_stack_bounds.stack_top = __msan_stack_bounds.stack_bottom = 1; - GetThreadStackTopAndBottom(/* at_initialization */false, - &__msan_stack_bounds.stack_top, - &__msan_stack_bounds.stack_bottom); - } - *stack_top = __msan_stack_bounds.stack_top; - *stack_bottom = __msan_stack_bounds.stack_bottom; -} - -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, +void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, bool request_fast_unwind) { - if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) { + MsanThread *t = GetCurrentThread(); + if (!t || !StackTrace::WillUseFastUnwind(request_fast_unwind)) { // Block reports from our interceptors during _Unwind_Backtrace. SymbolizerScope sym_scope; - return stack->Unwind(max_s, pc, bp, 0, 0, request_fast_unwind); + return stack->Unwind(max_s, pc, bp, 0, 0, 0, request_fast_unwind); } - uptr stack_top, stack_bottom; - GetCurrentStackBounds(&stack_top, &stack_bottom); - stack->Unwind(max_s, pc, bp, stack_top, stack_bottom, request_fast_unwind); + stack->Unwind(max_s, pc, bp, 0, t->stack_top(), t->stack_bottom(), + request_fast_unwind); } void PrintWarning(uptr pc, uptr bp) { PrintWarningWithOrigin(pc, bp, __msan_origin_tls); } -bool OriginIsValid(u32 origin) { - return origin != 0 && origin != (u32)-1; -} - void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { if (msan_expect_umr) { // Printf("Expected UMR\n"); @@ -205,15 +202,13 @@ void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { ++msan_report_count; - StackTrace stack; - GetStackTrace(&stack, kStackTraceMax, pc, bp, - common_flags()->fast_unwind_on_fatal); + GET_FATAL_STACK_TRACE_PC_BP(pc, bp); u32 report_origin = - (__msan_get_track_origins() && OriginIsValid(origin)) ? origin : 0; + (__msan_get_track_origins() && Origin::isValidId(origin)) ? origin : 0; ReportUMR(&stack, report_origin); - if (__msan_get_track_origins() && !OriginIsValid(origin)) { + if (__msan_get_track_origins() && !Origin::isValidId(origin)) { Printf( " ORIGIN: invalid (%x). Might be a bug in MemorySanitizer origin " "tracking.\n This could still be a bug in your code, too!\n", @@ -242,7 +237,8 @@ void ScopedThreadLocalStateBackup::Restore() { internal_memset(__msan_va_arg_tls, 0, sizeof(__msan_va_arg_tls)); if (__msan_get_track_origins()) { - internal_memset(&__msan_retval_origin_tls, 0, sizeof(__msan_retval_tls)); + internal_memset(&__msan_retval_origin_tls, 0, + sizeof(__msan_retval_origin_tls)); internal_memset(__msan_param_origin_tls, 0, sizeof(__msan_param_origin_tls)); } @@ -251,25 +247,71 @@ void ScopedThreadLocalStateBackup::Restore() { void UnpoisonThreadLocalState() { } -const char *GetOriginDescrIfStack(u32 id, uptr *pc) { - if ((id >> 31) == 0) return 0; - id &= (1U << 31) - 1; +const char *GetStackOriginDescr(u32 id, uptr *pc) { CHECK_LT(id, kNumStackOriginDescrs); if (pc) *pc = StackOriginPC[id]; return StackOriginDescr[id]; } +u32 ChainOrigin(u32 id, StackTrace *stack) { + MsanThread *t = GetCurrentThread(); + if (t && t->InSignalHandler()) + return id; + + Origin o = Origin::FromRawId(id); + Origin chained = Origin::CreateChainedOrigin(o, stack); + return chained.raw_id(); +} + } // namespace __msan // Interface. using namespace __msan; +#define MSAN_MAYBE_WARNING(type, size) \ + void __msan_maybe_warning_##size(type s, u32 o) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + if (UNLIKELY(s)) { \ + PrintWarningWithOrigin(pc, bp, o); \ + if (__msan::flags()->halt_on_error) { \ + Printf("Exiting\n"); \ + Die(); \ + } \ + } \ + } + +MSAN_MAYBE_WARNING(u8, 1) +MSAN_MAYBE_WARNING(u16, 2) +MSAN_MAYBE_WARNING(u32, 4) +MSAN_MAYBE_WARNING(u64, 8) + +#define MSAN_MAYBE_STORE_ORIGIN(type, size) \ + void __msan_maybe_store_origin_##size(type s, void *p, u32 o) { \ + if (UNLIKELY(s)) { \ + if (__msan_get_track_origins() > 1) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + GET_STORE_STACK_TRACE_PC_BP(pc, bp); \ + o = ChainOrigin(o, &stack); \ + } \ + *(u32 *)MEM_TO_ORIGIN((uptr)p & ~3UL) = o; \ + } \ + } + +MSAN_MAYBE_STORE_ORIGIN(u8, 1) +MSAN_MAYBE_STORE_ORIGIN(u16, 2) +MSAN_MAYBE_STORE_ORIGIN(u32, 4) +MSAN_MAYBE_STORE_ORIGIN(u64, 8) + void __msan_warning() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); if (__msan::flags()->halt_on_error) { + if (__msan::flags()->print_stats) + ReportStats(); Printf("Exiting\n"); Die(); } @@ -279,11 +321,14 @@ void __msan_warning_noreturn() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); + if (__msan::flags()->print_stats) + ReportStats(); Printf("Exiting\n"); Die(); } void __msan_init() { + CHECK(!msan_init_is_running); if (msan_inited) return; msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; @@ -293,6 +338,7 @@ void __msan_init() { const char *msan_options = GetEnv("MSAN_OPTIONS"); InitializeFlags(&msan_flags, msan_options); + if (common_flags()->help) PrintFlagDescriptions(); __sanitizer_set_report_path(common_flags()->log_path); InitializeInterceptors(); @@ -300,25 +346,21 @@ void __msan_init() { if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE) ReplaceOperatorsNewAndDelete(); + DisableCoreDumperIfNecessary(); if (StackSizeIsUnlimited()) { - if (common_flags()->verbosity) - Printf("Unlimited stack, doing reexec\n"); + VPrintf(1, "Unlimited stack, doing reexec\n"); // A reasonably large stack size. It is bigger than the usual 8Mb, because, // well, the program could have been run with unlimited stack for a reason. SetStackSizeLimitInBytes(32 * 1024 * 1024); ReExec(); } - if (common_flags()->verbosity) - Printf("MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); + VPrintf(1, "MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); - msan_running_under_dr = IsRunningUnderDr(); __msan_clear_on_return(); - if (__msan_get_track_origins() && common_flags()->verbosity > 0) - Printf("msan_track_origins\n"); - if (!InitShadow(/* prot1 */ false, /* prot2 */ true, /* map_shadow */ true, - __msan_get_track_origins())) { - // FIXME: prot1 = false is only required when running under DR. + if (__msan_get_track_origins()) + VPrintf(1, "msan_track_origins\n"); + if (!InitShadow(/* map_shadow */ true, __msan_get_track_origins())) { Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n"); Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n"); Printf("FATAL: Disabling ASLR is known to cause this error.\n"); @@ -328,19 +370,21 @@ void __msan_init() { Die(); } - const char *external_symbolizer = common_flags()->external_symbolizer_path; - bool external_symbolizer_started = - Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); - if (external_symbolizer && external_symbolizer[0]) { - CHECK(external_symbolizer_started); + Symbolizer::GetOrInit()->AddHooks(EnterSymbolizer, ExitSymbolizer); + + if (common_flags()->coverage) { + __sanitizer_cov_init(); + Atexit(__sanitizer_cov_dump); } - Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); - GetThreadStackTopAndBottom(/* at_initialization */true, - &__msan_stack_bounds.stack_top, - &__msan_stack_bounds.stack_bottom); - if (common_flags()->verbosity) - Printf("MemorySanitizer init done\n"); + MsanTSDInit(MsanTSDDtor); + + MsanThread *main_thread = MsanThread::Create(0, 0); + SetCurrentThread(main_thread); + main_thread->ThreadStart(); + + VPrintf(1, "MemorySanitizer init done\n"); + msan_init_is_running = 0; msan_inited = 1; } @@ -359,9 +403,7 @@ void __msan_set_expect_umr(int expect_umr) { } else if (!msan_expected_umr_found) { GET_CALLER_PC_BP_SP; (void)sp; - StackTrace stack; - GetStackTrace(&stack, kStackTraceMax, pc, bp, - common_flags()->fast_unwind_on_fatal); + GET_FATAL_STACK_TRACE_PC_BP(pc, bp); ReportExpectedUMRNotFound(&stack); Die(); } @@ -373,75 +415,66 @@ void __msan_print_shadow(const void *x, uptr size) { Printf("Not a valid application address: %p\n", x); return; } + + DescribeMemoryRange(x, size); +} + +void __msan_dump_shadow(const void *x, uptr size) { + if (!MEM_IS_APP(x)) { + Printf("Not a valid application address: %p\n", x); + return; + } + unsigned char *s = (unsigned char*)MEM_TO_SHADOW(x); - u32 *o = (u32*)MEM_TO_ORIGIN(x); for (uptr i = 0; i < size; i++) { +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + Printf("%x%x ", s[i] & 0xf, s[i] >> 4); +#else Printf("%x%x ", s[i] >> 4, s[i] & 0xf); - } - Printf("\n"); - if (__msan_get_track_origins()) { - for (uptr i = 0; i < size / 4; i++) { - Printf(" o: %x ", o[i]); - } - Printf("\n"); - } -} - -void __msan_print_param_shadow() { - for (int i = 0; i < 16; i++) { - Printf("#%d:%zx ", i, __msan_param_tls[i]); +#endif } Printf("\n"); } sptr __msan_test_shadow(const void *x, uptr size) { - unsigned char *s = (unsigned char*)MEM_TO_SHADOW((uptr)x); + if (!MEM_IS_APP(x)) return -1; + unsigned char *s = (unsigned char *)MEM_TO_SHADOW((uptr)x); for (uptr i = 0; i < size; ++i) if (s[i]) return i; return -1; } +void __msan_check_mem_is_initialized(const void *x, uptr size) { + if (!__msan::flags()->report_umrs) return; + sptr offset = __msan_test_shadow(x, size); + if (offset < 0) + return; + + GET_CALLER_PC_BP_SP; + (void)sp; + ReportUMRInsideAddressRange(__func__, x, size, offset); + __msan::PrintWarningWithOrigin(pc, bp, + __msan_get_origin(((const char *)x) + offset)); + if (__msan::flags()->halt_on_error) { + Printf("Exiting\n"); + Die(); + } +} + int __msan_set_poison_in_malloc(int do_poison) { int old = flags()->poison_in_malloc; flags()->poison_in_malloc = do_poison; return old; } -int __msan_has_dynamic_component() { - return msan_running_under_dr; -} +int __msan_has_dynamic_component() { return false; } NOINLINE void __msan_clear_on_return() { __msan_param_tls[0] = 0; } -static void* get_tls_base() { - u64 p; - asm("mov %%fs:0, %0" - : "=r"(p) ::); - return (void*)p; -} - -int __msan_get_retval_tls_offset() { - // volatile here is needed to avoid UB, because the compiler thinks that we - // are doing address arithmetics on unrelated pointers, and takes some - // shortcuts - volatile sptr retval_tls_p = (sptr)&__msan_retval_tls; - volatile sptr tls_base_p = (sptr)get_tls_base(); - return retval_tls_p - tls_base_p; -} - -int __msan_get_param_tls_offset() { - // volatile here is needed to avoid UB, because the compiler thinks that we - // are doing address arithmetics on unrelated pointers, and takes some - // shortcuts - volatile sptr param_tls_p = (sptr)&__msan_param_tls; - volatile sptr tls_base_p = (sptr)get_tls_base(); - return param_tls_p - tls_base_p; -} - void __msan_partial_poison(const void* data, void* shadow, uptr size) { internal_memcpy((void*)MEM_TO_SHADOW((uptr)data), shadow, size); } @@ -460,8 +493,8 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { uptr beg = x & ~3UL; // align down. uptr end = (x + size + 3) & ~3UL; // align up. u64 origin64 = ((u64)origin << 32) | origin; - // This is like memset, but the value is 32-bit. We unroll by 2 two write - // 64-bits at once. May want to unroll further to get 128-bit stores. + // This is like memset, but the value is 32-bit. We unroll by 2 to write + // 64 bits at once. May want to unroll further to get 128-bit stores. if (beg & 7ULL) { *(u32*)beg = origin; beg += 4; @@ -475,11 +508,11 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { // 'descr' is created at compile time and contains '----' in the beginning. // When we see descr for the first time we replace '----' with a uniq id // and set the origin to (id | (31-th bit)). -void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { +void __msan_set_alloca_origin(void *a, uptr size, char *descr) { __msan_set_alloca_origin4(a, size, descr, 0); } -void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) { +void __msan_set_alloca_origin4(void *a, uptr size, char *descr, uptr pc) { static const u32 dash = '-'; static const u32 first_timer = dash + (dash << 8) + (dash << 16) + (dash << 24); @@ -487,23 +520,25 @@ void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) { bool print = false; // internal_strstr(descr + 4, "AllocaTOTest") != 0; u32 id = *id_ptr; if (id == first_timer) { - id = atomic_fetch_add(&NumStackOriginDescrs, - 1, memory_order_relaxed); + u32 idx = atomic_fetch_add(&NumStackOriginDescrs, 1, memory_order_relaxed); + CHECK_LT(idx, kNumStackOriginDescrs); + StackOriginDescr[idx] = descr + 4; + StackOriginPC[idx] = pc; + id = Origin::CreateStackOrigin(idx).raw_id(); *id_ptr = id; - CHECK_LT(id, kNumStackOriginDescrs); - StackOriginDescr[id] = descr + 4; - StackOriginPC[id] = pc; if (print) - Printf("First time: id=%d %s %p \n", id, descr + 4, pc); + Printf("First time: idx=%d id=%d %s %p \n", idx, id, descr + 4, pc); } - id |= 1U << 31; if (print) Printf("__msan_set_alloca_origin: descr=%s id=%x\n", descr + 4, id); __msan_set_origin(a, size, id); } -const char *__msan_get_origin_descr_if_stack(u32 id) { - return GetOriginDescrIfStack(id, 0); +u32 __msan_chain_origin(u32 id) { + GET_CALLER_PC_BP_SP; + (void)sp; + GET_STORE_STACK_TRACE_PC_BP(pc, bp); + return ChainOrigin(id, &stack); } u32 __msan_get_origin(const void *a) { @@ -521,40 +556,50 @@ u32 __msan_get_umr_origin() { u16 __sanitizer_unaligned_load16(const uu16 *p) { __msan_retval_tls[0] = *(uu16 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) - __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } u32 __sanitizer_unaligned_load32(const uu32 *p) { __msan_retval_tls[0] = *(uu32 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) - __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } u64 __sanitizer_unaligned_load64(const uu64 *p) { __msan_retval_tls[0] = *(uu64 *)MEM_TO_SHADOW((uptr)p); if (__msan_get_track_origins()) - __msan_retval_origin_tls = *(uu32 *)MEM_TO_ORIGIN((uptr)p); + __msan_retval_origin_tls = GetOriginIfPoisoned((uptr)p, sizeof(*p)); return *p; } void __sanitizer_unaligned_store16(uu16 *p, u16 x) { - *(uu16 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; - if (__msan_get_track_origins()) - *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + u16 s = __msan_param_tls[1]; + *(uu16 *)MEM_TO_SHADOW((uptr)p) = s; + if (s && __msan_get_track_origins()) + if (uu32 o = __msan_param_origin_tls[2]) + SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } void __sanitizer_unaligned_store32(uu32 *p, u32 x) { - *(uu32 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; - if (__msan_get_track_origins()) - *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + u32 s = __msan_param_tls[1]; + *(uu32 *)MEM_TO_SHADOW((uptr)p) = s; + if (s && __msan_get_track_origins()) + if (uu32 o = __msan_param_origin_tls[2]) + SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } void __sanitizer_unaligned_store64(uu64 *p, u64 x) { - *(uu64 *)MEM_TO_SHADOW((uptr)p) = __msan_param_tls[1]; - if (__msan_get_track_origins()) - *(uu32 *)MEM_TO_ORIGIN((uptr)p) = __msan_param_origin_tls[1]; + u64 s = __msan_param_tls[1]; + *(uu64 *)MEM_TO_SHADOW((uptr)p) = s; + if (s && __msan_get_track_origins()) + if (uu32 o = __msan_param_origin_tls[2]) + SetOriginIfPoisoned((uptr)p, (uptr)&s, sizeof(s), o); *p = x; } +void __msan_set_death_callback(void (*callback)(void)) { + death_callback = callback; +} + #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE @@ -562,3 +607,10 @@ const char* __msan_default_options() { return ""; } } // extern "C" #endif +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_print_stack_trace() { + GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()); + stack.Print(); +} +} // extern "C" diff --git a/lib/msan/msan.h b/lib/msan/msan.h index 4e6c6194505e4..9bc1e4c6a4473 100644 --- a/lib/msan/msan.h +++ b/lib/msan/msan.h @@ -25,15 +25,94 @@ # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif -#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL) -#define SHADOW_TO_ORIGIN(shadow) (((uptr)shadow) + 0x200000000000ULL) -#define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW(mem))) -#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL) +/* +C/C++ on FreeBSD +0000 0000 0000 - 00ff ffff ffff: Low memory: main binary, MAP_32BIT mappings and modules +0100 0000 0000 - 0fff ffff ffff: Bad1 +1000 0000 0000 - 30ff ffff ffff: Shadow +3100 0000 0000 - 37ff ffff ffff: Bad2 +3800 0000 0000 - 58ff ffff ffff: Origins +5900 0000 0000 - 5fff ffff ffff: Bad3 +6000 0000 0000 - 7fff ffff ffff: High memory: heap, modules and main thread stack + +C/C++ on Linux/PIE +0000 0000 0000 - 1fff ffff ffff: Bad1 +2000 0000 0000 - 3fff ffff ffff: Shadow +4000 0000 0000 - 5fff ffff ffff: Origins +6000 0000 0000 - 7fff ffff ffff: Main memory + +C/C++ on Mips +0000 0000 0000 - 009f ffff ffff: Bad1 +00a0 0000 0000 - 00bf ffff ffff: Shadow +00c0 0000 0000 - 00df ffff ffff: Origins +00e0 0000 0000 - 00ff ffff ffff: Main memory +*/ + +#if SANITIZER_LINUX && defined(__mips64) +const uptr kLowMemBeg = 0; +const uptr kLowMemSize = 0; +const uptr kHighMemBeg = 0x00e000000000; +const uptr kHighMemSize = 0x002000000000; +const uptr kShadowBeg = 0x00a000000000; +const uptr kShadowSize = 0x002000000000; +const uptr kOriginsBeg = 0x00c000000000; +# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x4000000000ULL) +#elif SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 64 +const uptr kLowMemBeg = 0x000000000000; +const uptr kLowMemSize = 0x010000000000; +const uptr kHighMemBeg = 0x600000000000; +const uptr kHighMemSize = 0x200000000000; +const uptr kShadowBeg = 0x100000000000; +const uptr kShadowSize = 0x210000000000; +const uptr kOriginsBeg = 0x380000000000; +// Maps low and high app ranges to contiguous space with zero base: +// Low: 0000 0000 0000 - 00ff ffff ffff -> 2000 0000 0000 - 20ff ffff ffff +// High: 6000 0000 0000 - 7fff ffff ffff -> 0000 0000 0000 - 1fff ffff ffff +# define LINEARIZE_MEM(mem) \ + (((uptr)(mem) & ~0xc00000000000ULL) ^ 0x200000000000ULL) +# define MEM_TO_SHADOW(mem) (LINEARIZE_MEM((mem)) + 0x100000000000ULL) +#elif SANITIZER_LINUX && SANITIZER_WORDSIZE == 64 +const uptr kLowMemBeg = 0; +const uptr kLowMemSize = 0; +const uptr kHighMemBeg = 0x600000000000; +const uptr kHighMemSize = 0x200000000000; +const uptr kShadowBeg = 0x200000000000; +const uptr kShadowSize = 0x200000000000; +const uptr kOriginsBeg = 0x400000000000; +# define MEM_TO_SHADOW(mem) (((uptr)(mem)) & ~0x400000000000ULL) +#else +#error "Unsupported platform" +#endif + +const uptr kBad1Beg = kLowMemBeg + kLowMemSize; +const uptr kBad1Size = kShadowBeg - kBad1Beg; + +const uptr kBad2Beg = kShadowBeg + kShadowSize; +const uptr kBad2Size = kOriginsBeg - kBad2Beg; + +const uptr kOriginsSize = kShadowSize; + +const uptr kBad3Beg = kOriginsBeg + kOriginsSize; +const uptr kBad3Size = kHighMemBeg - kBad3Beg; + +#define SHADOW_TO_ORIGIN(shadow) \ + (((uptr)(shadow)) + (kOriginsBeg - kShadowBeg)) + +#define MEM_TO_ORIGIN(mem) (SHADOW_TO_ORIGIN(MEM_TO_SHADOW((mem)))) + +#define MEM_IS_APP(mem) \ + ((kLowMemSize > 0 && (uptr)(mem) < kLowMemSize) || \ + (uptr)(mem) >= kHighMemBeg) + #define MEM_IS_SHADOW(mem) \ - ((uptr)mem >= 0x200000000000ULL && (uptr)mem <= 0x400000000000ULL) + ((uptr)(mem) >= kShadowBeg && (uptr)(mem) < kShadowBeg + kShadowSize) -const int kMsanParamTlsSizeInWords = 100; -const int kMsanRetvalTlsSizeInWords = 100; +#define MEM_IS_ORIGIN(mem) \ + ((uptr)(mem) >= kOriginsBeg && (uptr)(mem) < kOriginsBeg + kOriginsSize) + +// These constants must be kept in sync with the ones in MemorySanitizer.cc. +const int kMsanParamTlsSize = 800; +const int kMsanRetvalTlsSize = 800; namespace __msan { extern int msan_inited; @@ -41,7 +120,7 @@ extern bool msan_init_is_running; extern int msan_report_count; bool ProtectRange(uptr beg, uptr end); -bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins); +bool InitShadow(bool map_shadow, bool init_origins); char *GetProcSelfMaps(); void InitializeInterceptors(); @@ -53,7 +132,7 @@ void InstallTrapHandler(); void InstallAtExitHandler(); void ReplaceOperatorsNewAndDelete(); -const char *GetOriginDescrIfStack(u32 id, uptr *pc); +const char *GetStackOriginDescr(u32 id, uptr *pc); void EnterSymbolizer(); void ExitSymbolizer(); @@ -64,31 +143,56 @@ struct SymbolizerScope { ~SymbolizerScope() { ExitSymbolizer(); } }; -void EnterLoader(); -void ExitLoader(); - void MsanDie(); void PrintWarning(uptr pc, uptr bp); void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin); -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, +void GetStackTrace(BufferedStackTrace *stack, uptr max_s, uptr pc, uptr bp, bool request_fast_unwind); void ReportUMR(StackTrace *stack, u32 origin); void ReportExpectedUMRNotFound(StackTrace *stack); +void ReportStats(); void ReportAtExitStatistics(); +void DescribeMemoryRange(const void *x, uptr size); +void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, + uptr offset); // Unpoison first n function arguments. void UnpoisonParam(uptr n); void UnpoisonThreadLocalState(); -#define GET_MALLOC_STACK_TRACE \ - StackTrace stack; \ - stack.size = 0; \ - if (__msan_get_track_origins() && msan_inited) \ - GetStackTrace(&stack, common_flags()->malloc_context_size, \ - StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ - common_flags()->fast_unwind_on_malloc) +u32 GetOriginIfPoisoned(uptr a, uptr size); +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, u32 src_origin); +void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack); +void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack); +void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack); + +// Returns a "chained" origin id, pointing to the given stack trace followed by +// the previous origin id. +u32 ChainOrigin(u32 id, StackTrace *stack); + +#define GET_MALLOC_STACK_TRACE \ + BufferedStackTrace stack; \ + if (__msan_get_track_origins() && msan_inited) \ + GetStackTrace(&stack, common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ + common_flags()->fast_unwind_on_malloc) + +#define GET_STORE_STACK_TRACE_PC_BP(pc, bp) \ + BufferedStackTrace stack; \ + if (__msan_get_track_origins() > 1 && msan_inited) \ + GetStackTrace(&stack, flags()->store_context_size, pc, bp, \ + common_flags()->fast_unwind_on_malloc) + +#define GET_FATAL_STACK_TRACE_PC_BP(pc, bp) \ + BufferedStackTrace stack; \ + if (msan_inited) \ + GetStackTrace(&stack, kStackTraceMax, pc, bp, \ + common_flags()->fast_unwind_on_fatal) + +#define GET_STORE_STACK_TRACE \ + GET_STORE_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) class ScopedThreadLocalStateBackup { public: @@ -99,11 +203,19 @@ class ScopedThreadLocalStateBackup { private: u64 va_arg_overflow_size_tls; }; + +extern void (*death_callback)(void); + +void MsanTSDInit(void (*destructor)(void *tsd)); +void *MsanTSDGet(); +void MsanTSDSet(void *tsd); +void MsanTSDDtor(void *tsd); + } // namespace __msan #define MSAN_MALLOC_HOOK(ptr, size) \ - if (&__msan_malloc_hook) __msan_malloc_hook(ptr, size) + if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(ptr, size) #define MSAN_FREE_HOOK(ptr) \ - if (&__msan_free_hook) __msan_free_hook(ptr) + if (&__sanitizer_free_hook) __sanitizer_free_hook(ptr) #endif // MSAN_H diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc index 2badf712188b8..f21d71409ce21 100644 --- a/lib/msan/msan_allocator.cc +++ b/lib/msan/msan_allocator.cc @@ -13,8 +13,11 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" -#include "sanitizer_common/sanitizer_stackdepot.h" +#include "sanitizer_common/sanitizer_allocator_interface.h" #include "msan.h" +#include "msan_allocator.h" +#include "msan_origin.h" +#include "msan_thread.h" namespace __msan { @@ -22,20 +25,47 @@ struct Metadata { uptr requested_size; }; -static const uptr kAllocatorSpace = 0x600000000000ULL; -static const uptr kAllocatorSize = 0x80000000000; // 8T. -static const uptr kMetadataSize = sizeof(Metadata); -static const uptr kMaxAllowedMallocSize = 8UL << 30; +struct MsanMapUnmapCallback { + void OnMap(uptr p, uptr size) const {} + void OnUnmap(uptr p, uptr size) const { + __msan_unpoison((void *)p, size); -typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, - DefaultSizeClassMap> PrimaryAllocator; + // We are about to unmap a chunk of user memory. + // Mark the corresponding shadow memory as not needed. + FlushUnneededShadowMemory(MEM_TO_SHADOW(p), size); + if (__msan_get_track_origins()) + FlushUnneededShadowMemory(MEM_TO_ORIGIN(p), size); + } +}; + +#if defined(__mips64) + static const uptr kMaxAllowedMallocSize = 2UL << 30; + static const uptr kRegionSizeLog = 20; + static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; + typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; + typedef CompactSizeClassMap SizeClassMap; + + typedef SizeClassAllocator32<0, SANITIZER_MMAP_RANGE_SIZE, sizeof(Metadata), + SizeClassMap, kRegionSizeLog, ByteMap, + MsanMapUnmapCallback> PrimaryAllocator; +#elif defined(__x86_64__) + static const uptr kAllocatorSpace = 0x600000000000ULL; + static const uptr kAllocatorSize = 0x80000000000; // 8T. + static const uptr kMetadataSize = sizeof(Metadata); + static const uptr kMaxAllowedMallocSize = 8UL << 30; + + typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, kMetadataSize, + DefaultSizeClassMap, + MsanMapUnmapCallback> PrimaryAllocator; +#endif typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; -typedef LargeMmapAllocator<> SecondaryAllocator; +typedef LargeMmapAllocator<MsanMapUnmapCallback> SecondaryAllocator; typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator> Allocator; -static THREADLOCAL AllocatorCache cache; static Allocator allocator; +static AllocatorCache fallback_allocator_cache; +static SpinMutex fallback_mutex; static int inited = 0; @@ -46,42 +76,55 @@ static inline void Init() { allocator.Init(); } -void MsanAllocatorThreadFinish() { - allocator.SwallowCache(&cache); +AllocatorCache *GetAllocatorCache(MsanThreadLocalMallocStorage *ms) { + CHECK(ms); + CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache)); + return reinterpret_cast<AllocatorCache *>(ms->allocator_cache); } -static void *MsanAllocate(StackTrace *stack, uptr size, - uptr alignment, bool zeroise) { +void MsanThreadLocalMallocStorage::CommitBack() { + allocator.SwallowCache(GetAllocatorCache(this)); +} + +static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, + bool zeroise) { Init(); if (size > kMaxAllowedMallocSize) { Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", (void *)size); return AllocatorReturnNull(); } - void *res = allocator.Allocate(&cache, size, alignment, false); - Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(res)); + MsanThread *t = GetCurrentThread(); + void *allocated; + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocated = allocator.Allocate(cache, size, alignment, false); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocated = allocator.Allocate(cache, size, alignment, false); + } + Metadata *meta = + reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated)); meta->requested_size = size; if (zeroise) { - __msan_clear_and_unpoison(res, size); + __msan_clear_and_unpoison(allocated, size); } else if (flags()->poison_in_malloc) { - __msan_poison(res, size); + __msan_poison(allocated, size); if (__msan_get_track_origins()) { - u32 stack_id = StackDepotPut(stack->trace, stack->size); - CHECK(stack_id); - CHECK_EQ((stack_id >> 31), - 0); // Higher bit is occupied by stack origins. - __msan_set_origin(res, size, stack_id); + Origin o = Origin::CreateHeapOrigin(stack); + __msan_set_origin(allocated, size, o.raw_id()); } } - MSAN_MALLOC_HOOK(res, size); - return res; + MSAN_MALLOC_HOOK(allocated, size); + return allocated; } void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); Init(); MSAN_FREE_HOOK(p); - Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(p)); + Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p)); uptr size = meta->requested_size; meta->requested_size = 0; // This memory will not be reused by anyone else, so we are free to keep it @@ -89,14 +132,19 @@ void MsanDeallocate(StackTrace *stack, void *p) { if (flags()->poison_in_free) { __msan_poison(p, size); if (__msan_get_track_origins()) { - u32 stack_id = StackDepotPut(stack->trace, stack->size); - CHECK(stack_id); - CHECK_EQ((stack_id >> 31), - 0); // Higher bit is occupied by stack origins. - __msan_set_origin(p, size, stack_id); + Origin o = Origin::CreateHeapOrigin(stack); + __msan_set_origin(p, size, o.raw_id()); } } - allocator.Deallocate(&cache, p); + MsanThread *t = GetCurrentThread(); + if (t) { + AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); + allocator.Deallocate(cache, p); + } else { + SpinMutexLock l(&fallback_mutex); + AllocatorCache *cache = &fallback_allocator_cache; + allocator.Deallocate(cache, p); + } } void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, @@ -128,12 +176,10 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, } static uptr AllocationSize(const void *p) { - if (p == 0) - return 0; + if (p == 0) return 0; const void *beg = allocator.GetBlockBegin(p); - if (beg != p) - return 0; - Metadata *b = (Metadata*)allocator.GetMetaData(p); + if (beg != p) return 0; + Metadata *b = (Metadata *)allocator.GetMetaData(p); return b->requested_size; } @@ -141,38 +187,24 @@ static uptr AllocationSize(const void *p) { using namespace __msan; -uptr __msan_get_current_allocated_bytes() { - u64 stats[AllocatorStatCount]; +uptr __sanitizer_get_current_allocated_bytes() { + uptr stats[AllocatorStatCount]; allocator.GetStats(stats); - u64 m = stats[AllocatorStatMalloced]; - u64 f = stats[AllocatorStatFreed]; - return m >= f ? m - f : 1; + return stats[AllocatorStatAllocated]; } -uptr __msan_get_heap_size() { - u64 stats[AllocatorStatCount]; +uptr __sanitizer_get_heap_size() { + uptr stats[AllocatorStatCount]; allocator.GetStats(stats); - u64 m = stats[AllocatorStatMmapped]; - u64 f = stats[AllocatorStatUnmapped]; - return m >= f ? m - f : 1; + return stats[AllocatorStatMapped]; } -uptr __msan_get_free_bytes() { - return 1; -} +uptr __sanitizer_get_free_bytes() { return 1; } -uptr __msan_get_unmapped_bytes() { - return 1; -} +uptr __sanitizer_get_unmapped_bytes() { return 1; } -uptr __msan_get_estimated_allocated_size(uptr size) { - return size; -} +uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } -int __msan_get_ownership(const void *p) { - return AllocationSize(p) != 0; -} +int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } -uptr __msan_get_allocated_size(const void *p) { - return AllocationSize(p); -} +uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } diff --git a/lib/msan/msan_allocator.h b/lib/msan/msan_allocator.h new file mode 100644 index 0000000000000..407942e54c1a9 --- /dev/null +++ b/lib/msan/msan_allocator.h @@ -0,0 +1,33 @@ +//===-- msan_allocator.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#ifndef MSAN_ALLOCATOR_H +#define MSAN_ALLOCATOR_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __msan { + +struct MsanThreadLocalMallocStorage { + uptr quarantine_cache[16]; + // Allocator cache contains atomic_uint64_t which must be 8-byte aligned. + ALIGNED(8) uptr allocator_cache[96 * (512 * 8 + 16)]; // Opaque. + void CommitBack(); + + private: + // These objects are allocated via mmap() and are zero-initialized. + MsanThreadLocalMallocStorage() {} +}; + +} // namespace __msan +#endif // MSAN_ALLOCATOR_H diff --git a/lib/msan/msan_chained_origin_depot.cc b/lib/msan/msan_chained_origin_depot.cc new file mode 100644 index 0000000000000..c21e8e82746aa --- /dev/null +++ b/lib/msan/msan_chained_origin_depot.cc @@ -0,0 +1,126 @@ +//===-- msan_chained_origin_depot.cc -----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A storage for chained origins. +//===----------------------------------------------------------------------===// + +#include "msan_chained_origin_depot.h" + +#include "sanitizer_common/sanitizer_stackdepotbase.h" + +namespace __msan { + +struct ChainedOriginDepotDesc { + u32 here_id; + u32 prev_id; +}; + +struct ChainedOriginDepotNode { + ChainedOriginDepotNode *link; + u32 id; + u32 here_id; + u32 prev_id; + + typedef ChainedOriginDepotDesc args_type; + bool eq(u32 hash, const args_type &args) const { + return here_id == args.here_id && prev_id == args.prev_id; + } + static uptr storage_size(const args_type &args) { + return sizeof(ChainedOriginDepotNode); + } + /* This is murmur2 hash for the 64->32 bit case. + It does not behave all that well because the keys have a very biased + distribution (I've seen 7-element buckets with the table only 14% full). + + here_id is built of + * (1 bits) Reserved, zero. + * (8 bits) Part id = bits 13..20 of the hash value of here_id's key. + * (23 bits) Sequential number (each part has each own sequence). + + prev_id has either the same distribution as here_id (but with 3:8:21) + split, or one of two reserved values (-1) or (-2). Either case can + dominate depending on the workload. + */ + static u32 hash(const args_type &args) { + const u32 m = 0x5bd1e995; + const u32 seed = 0x9747b28c; + const u32 r = 24; + u32 h = seed; + u32 k = args.here_id; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + + k = args.prev_id; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; + } + static bool is_valid(const args_type &args) { return true; } + void store(const args_type &args, u32 other_hash) { + here_id = args.here_id; + prev_id = args.prev_id; + } + args_type load() const { + args_type ret = {here_id, prev_id}; + return ret; + } + struct Handle { + ChainedOriginDepotNode *node_; + Handle() : node_(0) {} + explicit Handle(ChainedOriginDepotNode *node) : node_(node) {} + bool valid() { return node_; } + u32 id() { return node_->id; } + int here_id() { return node_->here_id; } + int prev_id() { return node_->prev_id; } + }; + Handle get_handle() { return Handle(this); } + + typedef Handle handle_type; +}; + +static StackDepotBase<ChainedOriginDepotNode, 4, 20> chainedOriginDepot; + +StackDepotStats *ChainedOriginDepotGetStats() { + return chainedOriginDepot.GetStats(); +} + +bool ChainedOriginDepotPut(u32 here_id, u32 prev_id, u32 *new_id) { + ChainedOriginDepotDesc desc = {here_id, prev_id}; + bool inserted; + ChainedOriginDepotNode::Handle h = chainedOriginDepot.Put(desc, &inserted); + *new_id = h.valid() ? h.id() : 0; + return inserted; +} + +// Retrieves a stored stack trace by the id. +u32 ChainedOriginDepotGet(u32 id, u32 *other) { + ChainedOriginDepotDesc desc = chainedOriginDepot.Get(id); + *other = desc.prev_id; + return desc.here_id; +} + +void ChainedOriginDepotLockAll() { + chainedOriginDepot.LockAll(); +} + +void ChainedOriginDepotUnlockAll() { + chainedOriginDepot.UnlockAll(); +} + +} // namespace __msan diff --git a/lib/msan/msan_chained_origin_depot.h b/lib/msan/msan_chained_origin_depot.h new file mode 100644 index 0000000000000..f7a71cef205ff --- /dev/null +++ b/lib/msan/msan_chained_origin_depot.h @@ -0,0 +1,29 @@ +//===-- msan_chained_origin_depot.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// A storage for chained origins. +//===----------------------------------------------------------------------===// +#ifndef MSAN_CHAINED_ORIGIN_DEPOT_H +#define MSAN_CHAINED_ORIGIN_DEPOT_H + +#include "sanitizer_common/sanitizer_common.h" + +namespace __msan { + +StackDepotStats *ChainedOriginDepotGetStats(); +bool ChainedOriginDepotPut(u32 here_id, u32 prev_id, u32 *new_id); +// Retrieves a stored stack trace by the id. +u32 ChainedOriginDepotGet(u32 id, u32 *other); + +void ChainedOriginDepotLockAll(); +void ChainedOriginDepotUnlockAll(); + +} // namespace __msan + +#endif // MSAN_CHAINED_ORIGIN_DEPOT_H diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h index 93fa8a60dba00..9b93f118a9856 100644 --- a/lib/msan/msan_flags.h +++ b/lib/msan/msan_flags.h @@ -19,13 +19,18 @@ namespace __msan { // Flags. struct Flags { int exit_code; + int origin_history_size; + int origin_history_per_stack_limit; bool poison_heap_with_zeroes; // default: false bool poison_stack_with_zeroes; // default: false bool poison_in_malloc; // default: true bool poison_in_free; // default: true bool report_umrs; bool wrap_signals; + bool print_stats; bool halt_on_error; + bool atexit; + int store_context_size; // like malloc_context_size, but for uninit stores }; Flags *flags(); diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 15a8bec12f319..bbdf18e162a38 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -17,14 +17,19 @@ #include "interception/interception.h" #include "msan.h" +#include "msan_chained_origin_depot.h" +#include "msan_origin.h" +#include "msan_thread.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_interface.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" #include "sanitizer_common/sanitizer_linux.h" +#include "sanitizer_common/sanitizer_tls_get_addr.h" #include <stdarg.h> // ACHTUNG! No other system header includes in this file. @@ -37,11 +42,15 @@ using __sanitizer::atomic_load; using __sanitizer::atomic_store; using __sanitizer::atomic_uintptr_t; -static unsigned g_thread_finalize_key; +#if SANITIZER_FREEBSD +#define __errno_location __error +#endif // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; +extern "C" int *__errno_location(void); + struct InterceptorScope { InterceptorScope() { ++in_interceptor_scope; } ~InterceptorScope() { --in_interceptor_scope; } @@ -59,22 +68,22 @@ 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::flags()->halt_on_error) { \ - 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; \ + ReportUMRInsideAddressRange(__func__, x, n, offset); \ + __msan::PrintWarningWithOrigin( \ + pc, bp, __msan_get_origin((const 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 @@ -84,9 +93,6 @@ bool IsInInterceptorScope() { 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); - INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(fread)(ptr, size, nmemb, file); @@ -95,6 +101,7 @@ INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { ENSURE_MSAN_INITED(); @@ -103,6 +110,10 @@ INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, __msan_unpoison(ptr, res *size); return res; } +#define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED INTERCEPT_FUNCTION(fread_unlocked) +#else +#define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED +#endif INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { ENSURE_MSAN_INITED(); @@ -152,12 +163,32 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { return 0; } +#if !SANITIZER_FREEBSD 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; } +#define MSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) +#else +#define MSAN_MAYBE_INTERCEPT_MEMALIGN +#endif + +INTERCEPTOR(void *, aligned_alloc, 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 *, __libc_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); + DTLS_on_libc_memalign(ptr, size * boundary); + return ptr; +} INTERCEPTOR(void *, valloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; @@ -165,6 +196,7 @@ INTERCEPTOR(void *, valloc, SIZE_T size) { return ptr; } +#if !SANITIZER_FREEBSD INTERCEPTOR(void *, pvalloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; uptr PageSize = GetPageSizeCached(); @@ -176,6 +208,10 @@ INTERCEPTOR(void *, pvalloc, SIZE_T size) { void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); return ptr; } +#define MSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) +#else +#define MSAN_MAYBE_INTERCEPT_PVALLOC +#endif INTERCEPTOR(void, free, void *ptr) { GET_MALLOC_STACK_TRACE; @@ -183,7 +219,55 @@ INTERCEPTOR(void, free, void *ptr) { MsanDeallocate(&stack, ptr); } +#if !SANITIZER_FREEBSD +INTERCEPTOR(void, cfree, void *ptr) { + GET_MALLOC_STACK_TRACE; + if (ptr == 0) return; + MsanDeallocate(&stack, ptr); +} +#define MSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree) +#else +#define MSAN_MAYBE_INTERCEPT_CFREE +#endif + +INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { + return __sanitizer_get_allocated_size(ptr); +} + +#if !SANITIZER_FREEBSD +// This function actually returns a struct by value, but we can't unpoison a +// temporary! The following is equivalent on all supported platforms, and we +// have a test to confirm that. +INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) { + REAL(memset)(sret, 0, sizeof(*sret)); + __msan_unpoison(sret, sizeof(*sret)); +} +#define MSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo) +#else +#define MSAN_MAYBE_INTERCEPT_MALLINFO +#endif + +#if !SANITIZER_FREEBSD +INTERCEPTOR(int, mallopt, int cmd, int value) { + return -1; +} +#define MSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt) +#else +#define MSAN_MAYBE_INTERCEPT_MALLOPT +#endif + +#if !SANITIZER_FREEBSD +INTERCEPTOR(void, malloc_stats, void) { + // FIXME: implement, but don't call REAL(malloc_stats)! +} +#define MSAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats) +#else +#define MSAN_MAYBE_INTERCEPT_MALLOC_STATS +#endif + INTERCEPTOR(SIZE_T, strlen, const char *s) { + if (msan_init_is_running) + return REAL(strlen)(s); ENSURE_MSAN_INITED(); SIZE_T res = REAL(strlen)(s); CHECK_UNPOISONED(s, res + 1); @@ -203,304 +287,261 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T n) { INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(strcpy)(dest, src); // NOLINT - __msan_copy_poison(dest, src, n + 1); + CopyPoison(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); if (copy_size < n) copy_size++; // trailing \0 char *res = REAL(strncpy)(dest, src, n); // NOLINT - __msan_copy_poison(dest, src, copy_size); + CopyPoison(dest, src, copy_size, &stack); + __msan_unpoison(dest + copy_size, n - copy_size); return res; } INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(stpcpy)(dest, src); // NOLINT - __msan_copy_poison(dest, src, n + 1); + CopyPoison(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(strdup)(src); - __msan_copy_poison(res, src, n + 1); + CopyPoison(res, src, n + 1, &stack); return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(char *, __strdup, char *src) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); char *res = REAL(__strdup)(src); - __msan_copy_poison(res, src, n + 1); + CopyPoison(res, src, n + 1, &stack); return res; } +#define MSAN_MAYBE_INTERCEPT___STRDUP INTERCEPT_FUNCTION(__strdup) +#else +#define MSAN_MAYBE_INTERCEPT___STRDUP +#endif INTERCEPTOR(char *, strndup, char *src, SIZE_T n) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strndup)(src, n); - __msan_copy_poison(res, src, copy_size); + CopyPoison(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(__strndup)(src, n); - __msan_copy_poison(res, src, copy_size); + CopyPoison(res, src, copy_size, &stack); __msan_unpoison(res + copy_size, 1); // \0 return res; } +#define MSAN_MAYBE_INTERCEPT___STRNDUP INTERCEPT_FUNCTION(__strndup) +#else +#define MSAN_MAYBE_INTERCEPT___STRNDUP +#endif INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) { ENSURE_MSAN_INITED(); char *res = REAL(gcvt)(number, ndigit, buf); - // DynamoRio tool will take care of unpoisoning gcvt result for us. - if (!__msan_has_dynamic_component()) { - SIZE_T n = REAL(strlen)(buf); - __msan_unpoison(buf, n + 1); - } + SIZE_T n = REAL(strlen)(buf); + __msan_unpoison(buf, n + 1); return res; } INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T src_size = REAL(strlen)(src); SIZE_T dest_size = REAL(strlen)(dest); char *res = REAL(strcat)(dest, src); // NOLINT - __msan_copy_poison(dest + dest_size, src, src_size + 1); + CopyPoison(dest + dest_size, src, src_size + 1, &stack); return res; } INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; SIZE_T dest_size = REAL(strlen)(dest); - SIZE_T copy_size = REAL(strlen)(src); - if (copy_size < n) - copy_size++; // trailing \0 + SIZE_T copy_size = REAL(strnlen)(src, n); char *res = REAL(strncat)(dest, src, n); // NOLINT - __msan_copy_poison(dest + dest_size, src, copy_size); - return res; -} - -INTERCEPTOR(long, strtol, const char *nptr, char **endptr, // NOLINT - int base) { - ENSURE_MSAN_INITED(); - long res = REAL(strtol)(nptr, endptr, base); // NOLINT - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); - } - return res; -} - -INTERCEPTOR(long long, strtoll, const char *nptr, char **endptr, // NOLINT - int base) { - ENSURE_MSAN_INITED(); - long res = REAL(strtoll)(nptr, endptr, base); //NOLINT - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); - } - return res; -} - -INTERCEPTOR(unsigned long, strtoul, const char *nptr, char **endptr, // NOLINT - int base) { - ENSURE_MSAN_INITED(); - unsigned long res = REAL(strtoul)(nptr, endptr, base); // NOLINT - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); - } - return res; -} - -INTERCEPTOR(unsigned long long, strtoull, const char *nptr, // NOLINT - char **endptr, int base) { - ENSURE_MSAN_INITED(); - unsigned long res = REAL(strtoull)(nptr, endptr, base); // NOLINT - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); - } - return res; -} - -INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT - ENSURE_MSAN_INITED(); - double res = REAL(strtod)(nptr, endptr); // 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(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 - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); - } + CopyPoison(dest + dest_size, src, copy_size, &stack); + __msan_unpoison(dest + dest_size + copy_size, 1); // \0 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)); - } +// Hack: always pass nptr and endptr as part of __VA_ARGS_ to avoid having to +// deal with empty __VA_ARGS__ in the case of INTERCEPTOR_STRTO. +#define INTERCEPTOR_STRTO_BODY(ret_type, func, ...) \ + ENSURE_MSAN_INITED(); \ + ret_type res = REAL(func)(__VA_ARGS__); \ + __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)); +#define INTERCEPTOR_STRTO(ret_type, func, char_type) \ + INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr) { \ + INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr); \ } - return res; -} -INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT - ENSURE_MSAN_INITED(); - long double res = REAL(strtold)(nptr, endptr); // NOLINT - if (!__msan_has_dynamic_component()) { - __msan_unpoison(endptr, sizeof(*endptr)); +#define INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ + INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ + int base) { \ + INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base); \ } - 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)); +#define INTERCEPTOR_STRTO_LOC(ret_type, func, char_type) \ + INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ + void *loc) { \ + INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, loc); \ } - 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)); +#define INTERCEPTOR_STRTO_BASE_LOC(ret_type, func, char_type) \ + INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ + int base, void *loc) { \ + INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base, loc); \ } - return res; -} -INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) { +#define INTERCEPTORS_STRTO(ret_type, func, char_type) \ + INTERCEPTOR_STRTO(ret_type, func, char_type) \ + INTERCEPTOR_STRTO_LOC(ret_type, func##_l, char_type) \ + INTERCEPTOR_STRTO_LOC(ret_type, __##func##_l, char_type) \ + INTERCEPTOR_STRTO_LOC(ret_type, __##func##_internal, char_type) + +#define INTERCEPTORS_STRTO_BASE(ret_type, func, char_type) \ + INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ + INTERCEPTOR_STRTO_BASE_LOC(ret_type, func##_l, char_type) \ + INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_l, char_type) \ + INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_internal, char_type) + +INTERCEPTORS_STRTO(double, strtod, char) // NOLINT +INTERCEPTORS_STRTO(float, strtof, char) // NOLINT +INTERCEPTORS_STRTO(long double, strtold, char) // NOLINT +INTERCEPTORS_STRTO_BASE(long, strtol, char) // NOLINT +INTERCEPTORS_STRTO_BASE(long long, strtoll, char) // NOLINT +INTERCEPTORS_STRTO_BASE(unsigned long, strtoul, char) // NOLINT +INTERCEPTORS_STRTO_BASE(unsigned long long, strtoull, char) // NOLINT + +INTERCEPTORS_STRTO(double, wcstod, wchar_t) // NOLINT +INTERCEPTORS_STRTO(float, wcstof, wchar_t) // NOLINT +INTERCEPTORS_STRTO(long double, wcstold, wchar_t) // NOLINT +INTERCEPTORS_STRTO_BASE(long, wcstol, wchar_t) // NOLINT +INTERCEPTORS_STRTO_BASE(long long, wcstoll, wchar_t) // NOLINT +INTERCEPTORS_STRTO_BASE(unsigned long, wcstoul, wchar_t) // NOLINT +INTERCEPTORS_STRTO_BASE(unsigned long long, wcstoull, wchar_t) // NOLINT + +#define INTERCEPT_STRTO(func) \ + INTERCEPT_FUNCTION(func); \ + INTERCEPT_FUNCTION(func##_l); \ + INTERCEPT_FUNCTION(__##func##_l); \ + INTERCEPT_FUNCTION(__##func##_internal); + + +// FIXME: support *wprintf in common format interceptors. +INTERCEPTOR(int, vswprintf, void *str, uptr size, void *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); + int res = REAL(vswprintf)(str, size, format, ap); + if (res >= 0) { + __msan_unpoison(str, 4 * (res + 1)); } return res; } -INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) { // NOLINT +INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { ENSURE_MSAN_INITED(); va_list ap; va_start(ap, format); - int res = vasprintf(strp, format, ap); // NOLINT + int res = vswprintf(str, size, format, ap); va_end(ap); return res; } -INTERCEPTOR(int, vsnprintf, char *str, uptr size, - const char *format, va_list ap) { +INTERCEPTOR(SIZE_T, strxfrm, char *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); - int res = REAL(vsnprintf)(str, size, format, ap); - if (res >= 0 && !__msan_has_dynamic_component()) { - __msan_unpoison(str, res + 1); - } + CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); + SIZE_T res = REAL(strxfrm)(dest, src, n); + if (res < n) __msan_unpoison(dest, res + 1); return res; } -INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { +INTERCEPTOR(SIZE_T, strxfrm_l, char *dest, const char *src, SIZE_T n, + void *loc) { ENSURE_MSAN_INITED(); - int res = REAL(vsprintf)(str, format, ap); - if (res >= 0 && !__msan_has_dynamic_component()) { - __msan_unpoison(str, res + 1); - } + CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); + SIZE_T res = REAL(strxfrm_l)(dest, src, n, loc); + if (res < n) __msan_unpoison(dest, res + 1); return res; } -INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { - ENSURE_MSAN_INITED(); - int res = REAL(vswprintf)(str, size, format, ap); - if (res >= 0 && !__msan_has_dynamic_component()) { - __msan_unpoison(str, 4 * (res + 1)); - } +#define INTERCEPTOR_STRFTIME_BODY(char_type, ret_type, func, s, ...) \ + ENSURE_MSAN_INITED(); \ + ret_type res = REAL(func)(s, __VA_ARGS__); \ + if (s) __msan_unpoison(s, sizeof(char_type) * (res + 1)); \ return res; -} -INTERCEPTOR(int, sprintf, char *str, const char *format, ...) { // NOLINT - ENSURE_MSAN_INITED(); - va_list ap; - va_start(ap, format); - int res = vsprintf(str, format, ap); // NOLINT - va_end(ap); - return res; +INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, + __sanitizer_tm *tm) { + INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime, s, max, format, tm); } -INTERCEPTOR(int, snprintf, char *str, uptr size, const char *format, ...) { - ENSURE_MSAN_INITED(); - va_list ap; - va_start(ap, format); - int res = vsnprintf(str, size, format, ap); - va_end(ap); - return res; +INTERCEPTOR(SIZE_T, strftime_l, char *s, SIZE_T max, const char *format, + __sanitizer_tm *tm, void *loc) { + INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime_l, s, max, format, tm, loc); } -INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { - ENSURE_MSAN_INITED(); - va_list ap; - va_start(ap, format); - int res = vswprintf(str, size, format, ap); - va_end(ap); - return res; +#if !SANITIZER_FREEBSD +INTERCEPTOR(SIZE_T, __strftime_l, char *s, SIZE_T max, const char *format, + __sanitizer_tm *tm, void *loc) { + INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, __strftime_l, s, max, format, tm, + loc); } +#define MSAN_MAYBE_INTERCEPT___STRFTIME_L INTERCEPT_FUNCTION(__strftime_l) +#else +#define MSAN_MAYBE_INTERCEPT___STRFTIME_L +#endif -// 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, +INTERCEPTOR(SIZE_T, wcsftime, wchar_t *s, SIZE_T max, const wchar_t *format, __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_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime, s, max, format, tm); +} + +INTERCEPTOR(SIZE_T, wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, + __sanitizer_tm *tm, void *loc) { + INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime_l, s, max, format, tm, + loc); +} + +#if !SANITIZER_FREEBSD +INTERCEPTOR(SIZE_T, __wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, + __sanitizer_tm *tm, void *loc) { + INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, __wcsftime_l, s, max, format, tm, + loc); } +#define MSAN_MAYBE_INTERCEPT___WCSFTIME_L INTERCEPT_FUNCTION(__wcsftime_l) +#else +#define MSAN_MAYBE_INTERCEPT___WCSFTIME_L +#endif INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); @@ -533,38 +574,42 @@ INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { // wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; wchar_t *res = REAL(wcscpy)(dest, src); - __msan_copy_poison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1)); + CopyPoison(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), &stack); return res; } // wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n); INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemcpy)(dest, src, n); - __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + CopyPoison(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmempcpy)(dest, src, n); - __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + CopyPoison(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); - wchar_t *res = (wchar_t *)fast_memset(s, c, n * sizeof(wchar_t)); + wchar_t *res = REAL(wmemset)(s, c, n); __msan_unpoison(s, n * sizeof(wchar_t)); return res; } INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemmove)(dest, src, n); - __msan_move_poison(dest, src, n * sizeof(wchar_t)); + MovePoison(dest, src, n * sizeof(wchar_t), &stack); return res; } @@ -574,13 +619,6 @@ INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) { return res; } -INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) { - ENSURE_MSAN_INITED(); - double res = REAL(wcstod)(nptr, endptr); - __msan_unpoison(endptr, sizeof(*endptr)); - return res; -} - INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); @@ -594,20 +632,18 @@ INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) { ENSURE_MSAN_INITED(); char *res = REAL(fcvt)(x, a, b, c); - if (!__msan_has_dynamic_component()) { - __msan_unpoison(b, sizeof(*b)); - __msan_unpoison(c, sizeof(*c)); - } + __msan_unpoison(b, sizeof(*b)); + __msan_unpoison(c, sizeof(*c)); + if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(char *, getenv, char *name) { + if (msan_init_is_running) + return REAL(getenv)(name); ENSURE_MSAN_INITED(); char *res = REAL(getenv)(name); - if (!__msan_has_dynamic_component()) { - if (res) - __msan_unpoison(res, REAL(strlen)(res) + 1); - } + if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } @@ -637,6 +673,7 @@ INTERCEPTOR(int, putenv, char *string) { return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); @@ -644,7 +681,12 @@ INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___FXSTAT INTERCEPT_FUNCTION(__fxstat) +#else +#define MSAN_MAYBE_INTERCEPT___FXSTAT +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat64)(magic, fd, buf); @@ -652,7 +694,12 @@ INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___FXSTAT64 INTERCEPT_FUNCTION(__fxstat64) +#else +#define MSAN_MAYBE_INTERCEPT___FXSTAT64 +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); @@ -660,7 +707,12 @@ INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___FXSTATAT INTERCEPT_FUNCTION(__fxstatat) +#else +#define MSAN_MAYBE_INTERCEPT___FXSTATAT +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); @@ -668,7 +720,12 @@ INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___FXSTATAT64 INTERCEPT_FUNCTION(__fxstatat64) +#else +#define MSAN_MAYBE_INTERCEPT___FXSTATAT64 +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); @@ -676,7 +733,12 @@ INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___XSTAT INTERCEPT_FUNCTION(__xstat) +#else +#define MSAN_MAYBE_INTERCEPT___XSTAT +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __xstat64, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat64)(magic, path, buf); @@ -684,7 +746,12 @@ INTERCEPTOR(int, __xstat64, int magic, char *path, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___XSTAT64 INTERCEPT_FUNCTION(__xstat64) +#else +#define MSAN_MAYBE_INTERCEPT___XSTAT64 +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __lxstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__lxstat)(magic, path, buf); @@ -692,7 +759,12 @@ INTERCEPTOR(int, __lxstat, int magic, char *path, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___LXSTAT INTERCEPT_FUNCTION(__lxstat) +#else +#define MSAN_MAYBE_INTERCEPT___LXSTAT +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, __lxstat64, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__lxstat64)(magic, path, buf); @@ -700,6 +772,10 @@ INTERCEPTOR(int, __lxstat64, int magic, char *path, void *buf) { __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } +#define MSAN_MAYBE_INTERCEPT___LXSTAT64 INTERCEPT_FUNCTION(__lxstat64) +#else +#define MSAN_MAYBE_INTERCEPT___LXSTAT64 +#endif INTERCEPTOR(int, pipe, int pipefd[2]) { if (msan_init_is_running) @@ -735,6 +811,7 @@ INTERCEPTOR(char *, fgets, char *s, int size, void *stream) { return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { ENSURE_MSAN_INITED(); char *res = REAL(fgets_unlocked)(s, size, stream); @@ -742,6 +819,10 @@ INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { __msan_unpoison(s, REAL(strlen)(s) + 1); return res; } +#define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED INTERCEPT_FUNCTION(fgets_unlocked) +#else +#define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED +#endif INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) @@ -753,6 +834,7 @@ INTERCEPTOR(int, getrlimit, int resource, void *rlim) { return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit64)(resource, rlim); @@ -762,6 +844,10 @@ INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { __msan_unpoison(rlim, __sanitizer::struct_rlimit64_sz); return res; } +#define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 INTERCEPT_FUNCTION(getrlimit64) +#else +#define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 +#endif INTERCEPTOR(int, uname, void *utsname) { ENSURE_MSAN_INITED(); @@ -784,6 +870,7 @@ INTERCEPTOR(int, gethostname, char *name, SIZE_T len) { return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents, int timeout) { ENSURE_MSAN_INITED(); @@ -793,7 +880,12 @@ INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents, } return res; } +#define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait) +#else +#define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT +#endif +#if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents, int timeout, void *sigmask) { ENSURE_MSAN_INITED(); @@ -803,6 +895,10 @@ INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents, } return res; } +#define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait) +#else +#define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT +#endif INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { ENSURE_MSAN_INITED(); @@ -861,30 +957,51 @@ void __msan_allocated_memory(const void* data, uptr size) { if (flags()->poison_in_malloc) __msan_poison(data, size); if (__msan_get_track_origins()) { - u32 stack_id = StackDepotPut(stack.trace, stack.size); - CHECK(stack_id); - CHECK_EQ((stack_id >> 31), 0); // Higher bit is occupied by stack origins. - __msan_set_origin(data, size, stack_id); + Origin o = Origin::CreateHeapOrigin(&stack); + __msan_set_origin(data, size, o.raw_id()); } } INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, int fd, OFF_T offset) { - ENSURE_MSAN_INITED(); + if (msan_init_is_running) + return REAL(mmap)(addr, length, prot, flags, fd, offset); + ENSURE_MSAN_INITED(); + if (addr && !MEM_IS_APP(addr)) { + if (flags & map_fixed) { + *__errno_location() = errno_EINVAL; + return (void *)-1; + } else { + addr = 0; + } + } void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } +#if !SANITIZER_FREEBSD INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, int fd, OFF64_T offset) { ENSURE_MSAN_INITED(); + if (addr && !MEM_IS_APP(addr)) { + if (flags & map_fixed) { + *__errno_location() = errno_EINVAL; + return (void *)-1; + } else { + addr = 0; + } + } void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } +#define MSAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64) +#else +#define MSAN_MAYBE_INTERCEPT_MMAP64 +#endif struct dlinfo { char *dli_fname; @@ -906,32 +1023,13 @@ INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { return res; } -INTERCEPTOR(char *, dlerror) { +INTERCEPTOR(char *, dlerror, int fake) { ENSURE_MSAN_INITED(); - char *res = REAL(dlerror)(); + char *res = REAL(dlerror)(fake); 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 -// initializes the module before returning, so without the dynamic component, we -// won't be able to clear the shadow before the initializers. Fixing this would -// require putting our own initializer first to clear the shadow. -INTERCEPTOR(void *, dlopen, const char *filename, int flag) { - ENSURE_MSAN_INITED(); - EnterLoader(); - link_map *map = (link_map *)REAL(dlopen)(filename, flag); - ExitLoader(); - if (!__msan_has_dynamic_component() && map) { - // If msandr didn't clear the shadow before the initializers ran, we do it - // ourselves afterwards. - ForEachMappedRegion(map, __msan_unpoison); - } - return (void *)map; -} - typedef int (*dl_iterate_phdr_cb)(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data); struct dl_iterate_phdr_data { @@ -953,12 +1051,10 @@ static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb callback, void *data) { ENSURE_MSAN_INITED(); - EnterLoader(); dl_iterate_phdr_data cbdata; cbdata.callback = callback; cbdata.data = data; int res = REAL(dl_iterate_phdr)(msan_dl_iterate_phdr_cb, (void *)&cbdata); - ExitLoader(); return res; } @@ -971,6 +1067,18 @@ INTERCEPTOR(int, getrusage, int who, void *usage) { return res; } +class SignalHandlerScope { + public: + SignalHandlerScope() { + if (MsanThread *t = GetCurrentThread()) + t->EnterSignalHandler(); + } + ~SignalHandlerScope() { + if (MsanThread *t = GetCurrentThread()) + t->LeaveSignalHandler(); + } +}; + // 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. @@ -979,6 +1087,7 @@ static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { + SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(1); @@ -989,6 +1098,7 @@ static void SignalHandler(int signo) { } static void SignalAction(int signo, void *si, void *uc) { + SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(3); __msan_unpoison(si, sizeof(__sanitizer_sigaction)); @@ -1013,21 +1123,21 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : 0; if (act) { - internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction)); - uptr cb = (uptr)pnew_act->sa_sigaction; + REAL(memcpy)(pnew_act, act, sizeof(__sanitizer_sigaction)); + uptr cb = (uptr)pnew_act->sigaction; uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) ? (uptr)SignalAction : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { atomic_store(&sigactions[signo], cb, memory_order_relaxed); - pnew_act->sa_sigaction = (void (*)(int, void *, void *))new_cb; + pnew_act->sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { - uptr cb = (uptr)oldact->sa_sigaction; + uptr cb = (uptr)oldact->sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - oldact->sa_sigaction = (void (*)(int, void *, void *))old_cb; + oldact->sigaction = (void (*)(int, void *, void *))old_cb; } } } else { @@ -1057,38 +1167,11 @@ 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_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); + MsanThread *t = (MsanThread *)arg; + SetCurrentThread(t); + return t->ThreadStart(); } INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), @@ -1100,18 +1183,11 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), attr = &myattr; } - AdjustStackSizeLinux(attr); + AdjustStackSize(attr); - ThreadParam p; - p.callback = callback; - p.param = param; - atomic_store(&p.done, 0, memory_order_relaxed); + MsanThread *t = MsanThread::Create(callback, param); - 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, MsanThreadStartFunc, t); if (attr == &myattr) pthread_attr_destroy(&myattr); @@ -1123,6 +1199,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, void (*dtor)(void *value)) { + if (msan_init_is_running) return REAL(pthread_key_create)(key, dtor); ENSURE_MSAN_INITED(); int res = REAL(pthread_key_create)(key, dtor); if (!res && key) @@ -1140,9 +1217,9 @@ INTERCEPTOR(int, pthread_join, void *th, void **retval) { extern char *tzname[2]; -INTERCEPTOR(void, tzset) { +INTERCEPTOR(void, tzset, int fake) { ENSURE_MSAN_INITED(); - REAL(tzset)(); + REAL(tzset)(fake); if (tzname[0]) __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); if (tzname[1]) @@ -1189,34 +1266,22 @@ INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { 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; +static void BeforeFork() { + StackDepotLockAll(); + ChainedOriginDepotLockAll(); } -INTERCEPTOR(int, mlockall, int flags) { - MlockIsUnsupported(); - return 0; +static void AfterFork() { + ChainedOriginDepotUnlockAll(); + StackDepotUnlockAll(); } -INTERCEPTOR(int, munlockall, void) { - MlockIsUnsupported(); - return 0; +INTERCEPTOR(int, fork, void) { + ENSURE_MSAN_INITED(); + BeforeFork(); + int pid = REAL(fork)(); + AfterFork(); + return pid; } struct MSanInterceptorContext { @@ -1232,8 +1297,6 @@ int OnExit() { } // 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) \ @@ -1242,21 +1305,20 @@ extern "C" int *__errno_location(void); 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"); \ +#define MSAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ + VReport(1, "MemorySanitizer: failed to intercept '" #name "'\n"); \ } while (0) #define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) -#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(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_INITIALIZE_RANGE(ctx, ptr, size) \ +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ @@ -1283,6 +1345,9 @@ extern "C" int *__errno_location(void); } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() +#define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, map) \ + if (map) ForEachMappedRegion((link_map *)map, __msan_unpoison); + #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) @@ -1295,155 +1360,204 @@ extern "C" int *__errno_location(void); #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" -// static -void *fast_memset(void *ptr, int c, SIZE_T n) { - // hack until we have a really fast internal_memset - if (sizeof(uptr) == 8 && - (n % 8) == 0 && - ((uptr)ptr % 8) == 0 && - (c == 0 || c == -1)) { - // Printf("memset %p %zd %x\n", ptr, n, c); - uptr to_store = c ? -1L : 0L; - uptr *p = (uptr*)ptr; - for (SIZE_T i = 0; i < n / 8; i++) - p[i] = to_store; - return ptr; - } - return internal_memset(ptr, c, n); -} - -// static -void *fast_memcpy(void *dst, const void *src, SIZE_T n) { - // Same hack as in fast_memset above. - if (sizeof(uptr) == 8 && - (n % 8) == 0 && - ((uptr)dst % 8) == 0 && - ((uptr)src % 8) == 0) { - uptr *d = (uptr*)dst; - uptr *s = (uptr*)src; - for (SIZE_T i = 0; i < n / 8; i++) - d[i] = s[i]; - return dst; +static void PoisonShadow(uptr ptr, uptr size, u8 value) { + uptr PageSize = GetPageSizeCached(); + uptr shadow_beg = MEM_TO_SHADOW(ptr); + uptr shadow_end = MEM_TO_SHADOW(ptr + size); + if (value || + shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { + REAL(memset)((void*)shadow_beg, value, shadow_end - shadow_beg); + } else { + uptr page_beg = RoundUpTo(shadow_beg, PageSize); + uptr page_end = RoundDownTo(shadow_end, PageSize); + + if (page_beg >= page_end) { + REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); + } else { + if (page_beg != shadow_beg) { + REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); + } + if (page_end != shadow_end) { + REAL(memset)((void *)page_end, 0, shadow_end - page_end); + } + MmapFixedNoReserve(page_beg, page_end - page_beg); + } } - return internal_memcpy(dst, src, n); } // These interface functions reside here so that they can use -// fast_memset, etc. +// REAL(memset), etc. void __msan_unpoison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); + PoisonShadow((uptr)a, size, 0); } void __msan_poison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; - fast_memset((void*)MEM_TO_SHADOW((uptr)a), - __msan::flags()->poison_heap_with_zeroes ? 0 : -1, size); + PoisonShadow((uptr)a, size, + __msan::flags()->poison_heap_with_zeroes ? 0 : -1); } void __msan_poison_stack(void *a, uptr size) { if (!MEM_IS_APP(a)) return; - fast_memset((void*)MEM_TO_SHADOW((uptr)a), - __msan::flags()->poison_stack_with_zeroes ? 0 : -1, size); + PoisonShadow((uptr)a, size, + __msan::flags()->poison_stack_with_zeroes ? 0 : -1); } void __msan_clear_and_unpoison(void *a, uptr size) { - fast_memset(a, 0, size); - fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); + REAL(memset)(a, 0, size); + PoisonShadow((uptr)a, size, 0); +} + +void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { + if (!msan_inited) return internal_memcpy(dest, src, n); + if (msan_init_is_running) return REAL(memcpy)(dest, src, n); + ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; + void *res = REAL(memcpy)(dest, src, n); + CopyPoison(dest, src, n, &stack); + return res; +} + +void *__msan_memset(void *s, int c, SIZE_T n) { + if (!msan_inited) return internal_memset(s, c, n); + if (msan_init_is_running) return REAL(memset)(s, c, n); + ENSURE_MSAN_INITED(); + void *res = REAL(memset)(s, c, n); + __msan_unpoison(s, n); + return res; +} + +void *__msan_memmove(void *dest, const void *src, SIZE_T n) { + if (!msan_inited) return internal_memmove(dest, src, n); + if (msan_init_is_running) return REAL(memmove)(dest, src, n); + ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; + void *res = REAL(memmove)(dest, src, n); + MovePoison(dest, src, n, &stack); + return res; +} + +void __msan_unpoison_string(const char* s) { + if (!MEM_IS_APP(s)) return; + __msan_unpoison(s, REAL(strlen)(s) + 1); } -u32 get_origin_if_poisoned(uptr a, uptr size) { - unsigned char *s = (unsigned char *)MEM_TO_SHADOW(a); +namespace __msan { + +u32 GetOriginIfPoisoned(uptr addr, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); for (uptr i = 0; i < size; ++i) if (s[i]) - return *(u32 *)SHADOW_TO_ORIGIN((s + i) & ~3UL); + return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); return 0; } -void __msan_copy_origin(void *dst, const void *src, uptr size) { +void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, + u32 src_origin) { + uptr dst_s = MEM_TO_SHADOW(addr); + uptr src_s = src_shadow; + uptr src_s_end = src_s + size; + + for (; src_s < src_s_end; ++dst_s, ++src_s) + if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s &~3UL) = src_origin; +} + +void CopyOrigin(void *dst, const void *src, uptr size, StackTrace *stack) { if (!__msan_get_track_origins()) return; if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; + 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 o = GetOriginIfPoisoned((uptr)src, d - beg); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); *(u32 *)MEM_TO_ORIGIN(beg) = o; + } beg += 4; } - uptr end = (d + size + 3) & ~3UL; + uptr end = (d + size) & ~3UL; + // If both ends fall into the same 4-byte slot, we are done. + if (end < beg) return; + // 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 (end < d + size) { + u32 o = GetOriginIfPoisoned((uptr)src + (end - d), (d + size) - end); + if (o) { + if (__msan_get_track_origins() > 1) o = ChainOrigin(o, stack); + *(u32 *)MEM_TO_ORIGIN(end) = o; + } } 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); + // FIXME: factor out to msan_copy_origin_aligned + if (__msan_get_track_origins() > 1) { + u32 *src = (u32 *)MEM_TO_ORIGIN(s); + u32 *src_s = (u32 *)MEM_TO_SHADOW(s); + u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); + u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); + u32 src_o = 0; + u32 dst_o = 0; + for (; src < src_end; ++src, ++src_s, ++dst) { + if (!*src_s) continue; + if (*src != src_o) { + src_o = *src; + dst_o = ChainOrigin(src_o, stack); + } + *dst = dst_o; + } + } else { + REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), + end - beg); + } } } -void __msan_copy_poison(void *dst, const void *src, uptr size) { +void MovePoison(void *dst, const void *src, uptr size, StackTrace *stack) { if (!MEM_IS_APP(dst)) return; if (!MEM_IS_APP(src)) return; - fast_memcpy((void*)MEM_TO_SHADOW((uptr)dst), - (void*)MEM_TO_SHADOW((uptr)src), size); - __msan_copy_origin(dst, src, size); + if (src == dst) return; + REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + CopyOrigin(dst, src, size, stack); } -void __msan_move_poison(void *dst, const void *src, uptr size) { +void CopyPoison(void *dst, const void *src, uptr size, StackTrace *stack) { if (!MEM_IS_APP(dst)) return; if (!MEM_IS_APP(src)) return; - internal_memmove((void*)MEM_TO_SHADOW((uptr)dst), - (void*)MEM_TO_SHADOW((uptr)src), size); - __msan_copy_origin(dst, src, size); -} - -void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { - ENSURE_MSAN_INITED(); - void *res = fast_memcpy(dest, src, n); - __msan_copy_poison(dest, src, n); - return res; -} - -void *__msan_memset(void *s, int c, SIZE_T n) { - ENSURE_MSAN_INITED(); - void *res = fast_memset(s, c, n); - __msan_unpoison(s, n); - return res; -} - -void *__msan_memmove(void *dest, const void *src, SIZE_T n) { - ENSURE_MSAN_INITED(); - void *res = REAL(memmove)(dest, src, n); - __msan_move_poison(dest, src, n); - return res; + REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), + (void *)MEM_TO_SHADOW((uptr)src), size); + CopyOrigin(dst, src, size, stack); } -namespace __msan { void InitializeInterceptors() { static int inited = 0; CHECK_EQ(inited, 0); - SANITIZER_COMMON_INTERCEPTORS_INIT; + InitializeCommonInterceptors(); INTERCEPT_FUNCTION(mmap); - INTERCEPT_FUNCTION(mmap64); + MSAN_MAYBE_INTERCEPT_MMAP64; INTERCEPT_FUNCTION(posix_memalign); - INTERCEPT_FUNCTION(memalign); + MSAN_MAYBE_INTERCEPT_MEMALIGN; + INTERCEPT_FUNCTION(__libc_memalign); INTERCEPT_FUNCTION(valloc); - INTERCEPT_FUNCTION(pvalloc); + MSAN_MAYBE_INTERCEPT_PVALLOC; INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(free); + MSAN_MAYBE_INTERCEPT_CFREE; + INTERCEPT_FUNCTION(malloc_usable_size); + MSAN_MAYBE_INTERCEPT_MALLINFO; + MSAN_MAYBE_INTERCEPT_MALLOPT; + MSAN_MAYBE_INTERCEPT_MALLOC_STATS; INTERCEPT_FUNCTION(fread); - INTERCEPT_FUNCTION(fread_unlocked); + MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED; INTERCEPT_FUNCTION(readlink); INTERCEPT_FUNCTION(memcpy); INTERCEPT_FUNCTION(memccpy); @@ -1458,73 +1572,73 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(strcpy); // NOLINT INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); - INTERCEPT_FUNCTION(__strdup); + MSAN_MAYBE_INTERCEPT___STRDUP; INTERCEPT_FUNCTION(strndup); - INTERCEPT_FUNCTION(__strndup); + MSAN_MAYBE_INTERCEPT___STRNDUP; INTERCEPT_FUNCTION(strncpy); // NOLINT INTERCEPT_FUNCTION(strlen); INTERCEPT_FUNCTION(strnlen); INTERCEPT_FUNCTION(gcvt); INTERCEPT_FUNCTION(strcat); // NOLINT INTERCEPT_FUNCTION(strncat); // NOLINT - INTERCEPT_FUNCTION(strtol); - INTERCEPT_FUNCTION(strtoll); - 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_STRTO(strtod); + INTERCEPT_STRTO(strtof); + INTERCEPT_STRTO(strtold); + INTERCEPT_STRTO(strtol); + INTERCEPT_STRTO(strtoul); + INTERCEPT_STRTO(strtoll); + INTERCEPT_STRTO(strtoull); + INTERCEPT_STRTO(wcstod); + INTERCEPT_STRTO(wcstof); + INTERCEPT_STRTO(wcstold); + INTERCEPT_STRTO(wcstol); + INTERCEPT_STRTO(wcstoul); + INTERCEPT_STRTO(wcstoll); + INTERCEPT_STRTO(wcstoull); INTERCEPT_FUNCTION(vswprintf); - INTERCEPT_FUNCTION(sprintf); // NOLINT - INTERCEPT_FUNCTION(snprintf); INTERCEPT_FUNCTION(swprintf); + INTERCEPT_FUNCTION(strxfrm); + INTERCEPT_FUNCTION(strxfrm_l); INTERCEPT_FUNCTION(strftime); + INTERCEPT_FUNCTION(strftime_l); + MSAN_MAYBE_INTERCEPT___STRFTIME_L; + INTERCEPT_FUNCTION(wcsftime); + INTERCEPT_FUNCTION(wcsftime_l); + MSAN_MAYBE_INTERCEPT___WCSFTIME_L; 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); + MSAN_MAYBE_INTERCEPT___FXSTAT; + MSAN_MAYBE_INTERCEPT___FXSTATAT; + MSAN_MAYBE_INTERCEPT___XSTAT; + MSAN_MAYBE_INTERCEPT___LXSTAT; + MSAN_MAYBE_INTERCEPT___FXSTAT64; + MSAN_MAYBE_INTERCEPT___FXSTATAT64; + MSAN_MAYBE_INTERCEPT___XSTAT64; + MSAN_MAYBE_INTERCEPT___LXSTAT64; INTERCEPT_FUNCTION(pipe); INTERCEPT_FUNCTION(pipe2); INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); - INTERCEPT_FUNCTION(fgets_unlocked); + MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED; INTERCEPT_FUNCTION(getrlimit); - INTERCEPT_FUNCTION(getrlimit64); + MSAN_MAYBE_INTERCEPT_GETRLIMIT64; INTERCEPT_FUNCTION(uname); INTERCEPT_FUNCTION(gethostname); - INTERCEPT_FUNCTION(epoll_wait); - INTERCEPT_FUNCTION(epoll_pwait); + MSAN_MAYBE_INTERCEPT_EPOLL_WAIT; + MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(recv); INTERCEPT_FUNCTION(recvfrom); INTERCEPT_FUNCTION(dladdr); INTERCEPT_FUNCTION(dlerror); - INTERCEPT_FUNCTION(dlopen); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); @@ -1535,11 +1649,7 @@ void InitializeInterceptors() { 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(); - } + INTERCEPT_FUNCTION(fork); inited = 1; } diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h index 794b3540f9313..8641f814bc5f8 100644 --- a/lib/msan/msan_interface_internal.h +++ b/lib/msan/msan_interface_internal.h @@ -38,8 +38,28 @@ SANITIZER_INTERFACE_ATTRIBUTE __attribute__((noreturn)) void __msan_warning_noreturn(); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_1(u8 s, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_2(u16 s, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_4(u32 s, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_warning_8(u64 s, u32 o); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_store_origin_1(u8 s, void *p, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_store_origin_2(u16 s, void *p, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_store_origin_4(u32 s, void *p, u32 o); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_maybe_store_origin_8(u64 s, void *p, u32 o); + +SANITIZER_INTERFACE_ATTRIBUTE void __msan_unpoison(const void *a, uptr size); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_unpoison_string(const char *s); +SANITIZER_INTERFACE_ATTRIBUTE void __msan_clear_and_unpoison(void *a, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void* __msan_memcpy(void *dst, const void *src, uptr size); @@ -48,12 +68,6 @@ void* __msan_memset(void *s, int c, uptr n); SANITIZER_INTERFACE_ATTRIBUTE void* __msan_memmove(void* dest, const void* src, uptr n); SANITIZER_INTERFACE_ATTRIBUTE -void __msan_copy_poison(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_copy_origin(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE -void __msan_move_poison(void *dst, const void *src, uptr size); -SANITIZER_INTERFACE_ATTRIBUTE void __msan_poison(const void *a, uptr size); SANITIZER_INTERFACE_ATTRIBUTE void __msan_poison_stack(void *a, uptr size); @@ -69,11 +83,16 @@ SANITIZER_INTERFACE_ATTRIBUTE sptr __msan_test_shadow(const void *x, uptr size); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_check_mem_is_initialized(const void *x, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_origin(const void *a, uptr size, u32 origin); SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_alloca_origin(void *a, uptr size, const char *descr); +void __msan_set_alloca_origin(void *a, uptr size, char *descr); +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_alloca_origin4(void *a, uptr size, char *descr, uptr pc); SANITIZER_INTERFACE_ATTRIBUTE -void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc); +u32 __msan_chain_origin(u32 id); SANITIZER_INTERFACE_ATTRIBUTE u32 __msan_get_origin(const void *a); @@ -99,26 +118,14 @@ void __msan_set_expect_umr(int expect_umr); SANITIZER_INTERFACE_ATTRIBUTE void __msan_print_shadow(const void *x, uptr size); SANITIZER_INTERFACE_ATTRIBUTE -void __msan_print_param_shadow(); +void __msan_dump_shadow(const void *x, uptr size); SANITIZER_INTERFACE_ATTRIBUTE int __msan_has_dynamic_component(); -// Returns x such that %fs:x is the first byte of __msan_retval_tls. -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_retval_tls_offset(); -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_param_tls_offset(); - -// For intercepting mmap from ld.so in msandr. -SANITIZER_INTERFACE_ATTRIBUTE -bool __msan_is_in_loader(); - // For testing. SANITIZER_INTERFACE_ATTRIBUTE u32 __msan_get_umr_origin(); SANITIZER_INTERFACE_ATTRIBUTE -const char *__msan_get_origin_descr_if_stack(u32 id); -SANITIZER_INTERFACE_ATTRIBUTE void __msan_partial_poison(const void* data, void* shadow, uptr size); // Tell MSan about newly allocated memory (ex.: custom allocator). @@ -145,31 +152,7 @@ SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_unaligned_store64(uu64 *p, u64 x); SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_estimated_allocated_size(uptr size); - -SANITIZER_INTERFACE_ATTRIBUTE -int __msan_get_ownership(const void *p); - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_allocated_size(const void *p); - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_current_allocated_bytes(); - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_heap_size(); - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_free_bytes(); - -SANITIZER_INTERFACE_ATTRIBUTE -uptr __msan_get_unmapped_bytes(); - -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -/* OPTIONAL */ void __msan_malloc_hook(void *ptr, uptr size); - -SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE -/* OPTIONAL */ void __msan_free_hook(void *ptr); +void __msan_set_death_callback(void (*callback)(void)); } // extern "C" #endif // MSAN_INTERFACE_INTERNAL_H diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc index 46f501e488c52..0b67b531d51ca 100644 --- a/lib/msan/msan_linux.cc +++ b/lib/msan/msan_linux.cc @@ -9,16 +9,18 @@ // // This file is a part of MemorySanitizer. // -// Linux-specific code. +// Linux- and FreeBSD-specific code. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" -#if SANITIZER_LINUX +#if SANITIZER_FREEBSD || SANITIZER_LINUX #include "msan.h" +#include "msan_thread.h" #include <elf.h> #include <link.h> +#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <signal.h> @@ -33,63 +35,117 @@ namespace __msan { -static const uptr kMemBeg = 0x600000000000; -static const uptr kMemEnd = 0x7fffffffffff; -static const uptr kShadowBeg = MEM_TO_SHADOW(kMemBeg); -static const uptr kShadowEnd = MEM_TO_SHADOW(kMemEnd); -static const uptr kBad1Beg = 0x100000000; // 4G -static const uptr kBad1End = kShadowBeg - 1; -static const uptr kBad2Beg = kShadowEnd + 1; -static const uptr kBad2End = kMemBeg - 1; -static const uptr kOriginsBeg = kBad2Beg; -static const uptr kOriginsEnd = kBad2End; - -bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins) { - if ((uptr) & InitShadow < kMemBeg) { - Printf("FATAL: Code below application range: %p < %p. Non-PIE build?\n", - &InitShadow, (void *)kMemBeg); - return false; +void ReportMapRange(const char *descr, uptr beg, uptr size) { + if (size > 0) { + uptr end = beg + size - 1; + VPrintf(1, "%s : %p - %p\n", descr, beg, end); } +} - if (common_flags()->verbosity) { - Printf("__msan_init %p\n", &__msan_init); - Printf("Memory : %p %p\n", kMemBeg, kMemEnd); - Printf("Bad2 : %p %p\n", kBad2Beg, kBad2End); - Printf("Origins : %p %p\n", kOriginsBeg, kOriginsEnd); - Printf("Shadow : %p %p\n", kShadowBeg, kShadowEnd); - Printf("Bad1 : %p %p\n", kBad1Beg, kBad1End); +static bool CheckMemoryRangeAvailability(uptr beg, uptr size) { + if (size > 0) { + uptr end = beg + size - 1; + if (!MemoryRangeIsAvailable(beg, end)) { + Printf("FATAL: Memory range %p - %p is not available.\n", beg, end); + return false; + } } + return true; +} - if (!MemoryRangeIsAvailable(kShadowBeg, - init_origins ? kOriginsEnd : kShadowEnd)) { - Printf("FATAL: Shadow memory range is not available.\n"); +static bool ProtectMemoryRange(uptr beg, uptr size) { + if (size > 0) { + uptr end = beg + size - 1; + if (!Mprotect(beg, size)) { + Printf("FATAL: Cannot protect memory range %p - %p.\n", beg, end); + return false; + } + } + return true; +} + +bool InitShadow(bool map_shadow, bool init_origins) { + // Let user know mapping parameters first. + VPrintf(1, "__msan_init %p\n", &__msan_init); + ReportMapRange("Low Memory ", kLowMemBeg, kLowMemSize); + ReportMapRange("Bad1 ", kBad1Beg, kBad1Size); + ReportMapRange("Shadow ", kShadowBeg, kShadowSize); + ReportMapRange("Bad2 ", kBad2Beg, kBad2Size); + ReportMapRange("Origins ", kOriginsBeg, kOriginsSize); + ReportMapRange("Bad3 ", kBad3Beg, kBad3Size); + ReportMapRange("High Memory", kHighMemBeg, kHighMemSize); + + // Check mapping sanity (the invariant). + CHECK_EQ(kLowMemBeg, 0); + CHECK_EQ(kBad1Beg, kLowMemBeg + kLowMemSize); + CHECK_EQ(kShadowBeg, kBad1Beg + kBad1Size); + CHECK_GT(kShadowSize, 0); + CHECK_GE(kShadowSize, kLowMemSize + kHighMemSize); + CHECK_EQ(kBad2Beg, kShadowBeg + kShadowSize); + CHECK_EQ(kOriginsBeg, kBad2Beg + kBad2Size); + CHECK_EQ(kOriginsSize, kShadowSize); + CHECK_EQ(kBad3Beg, kOriginsBeg + kOriginsSize); + CHECK_EQ(kHighMemBeg, kBad3Beg + kBad3Size); + CHECK_GT(kHighMemSize, 0); + CHECK_GE(kHighMemBeg + kHighMemSize, kHighMemBeg); // Tests for no overflow. + + if (kLowMemSize > 0) { + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg))); + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kLowMemBeg + kLowMemSize - 1))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kLowMemBeg + kLowMemSize - 1))); + } + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg))); + CHECK(MEM_IS_SHADOW(MEM_TO_SHADOW(kHighMemBeg + kHighMemSize - 1))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg))); + CHECK(MEM_IS_ORIGIN(MEM_TO_ORIGIN(kHighMemBeg + kHighMemSize - 1))); + + if (!MEM_IS_APP(&__msan_init)) { + Printf("FATAL: Code %p is out of application range. Non-PIE build?\n", + (uptr)&__msan_init); return false; } - if (prot1 && !Mprotect(kBad1Beg, kBad1End - kBad1Beg)) + if (!CheckMemoryRangeAvailability(kShadowBeg, kShadowSize) || + (init_origins && + !CheckMemoryRangeAvailability(kOriginsBeg, kOriginsSize)) || + !CheckMemoryRangeAvailability(kBad1Beg, kBad1Size) || + !CheckMemoryRangeAvailability(kBad2Beg, kBad2Size) || + !CheckMemoryRangeAvailability(kBad3Beg, kBad3Size)) { return false; - if (prot2 && !Mprotect(kBad2Beg, kBad2End - kBad2Beg)) + } + + if (!ProtectMemoryRange(kBad1Beg, kBad1Size) || + !ProtectMemoryRange(kBad2Beg, kBad2Size) || + !ProtectMemoryRange(kBad3Beg, kBad3Size)) { return false; + } + if (map_shadow) { - void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowEnd - kShadowBeg); + void *shadow = MmapFixedNoReserve(kShadowBeg, kShadowSize); if (shadow != (void*)kShadowBeg) return false; } if (init_origins) { - void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsEnd - kOriginsBeg); + void *origins = MmapFixedNoReserve(kOriginsBeg, kOriginsSize); if (origins != (void*)kOriginsBeg) return false; } return true; } void MsanDie() { + if (common_flags()->coverage) + __sanitizer_cov_dump(); + if (death_callback) + death_callback(); _exit(flags()->exit_code); } static void MsanAtExit(void) { + if (flags()->print_stats && (flags()->atexit || msan_report_count > 0)) + ReportStats(); if (msan_report_count > 0) { ReportAtExitStatistics(); - if (flags()->exit_code) - _exit(flags()->exit_code); + if (flags()->exit_code) _exit(flags()->exit_code); } } @@ -97,6 +153,36 @@ void InstallAtExitHandler() { atexit(MsanAtExit); } +// ---------------------- TSD ---------------- {{{1 + +static pthread_key_t tsd_key; +static bool tsd_key_inited = false; +void MsanTSDInit(void (*destructor)(void *tsd)) { + CHECK(!tsd_key_inited); + tsd_key_inited = true; + CHECK_EQ(0, pthread_key_create(&tsd_key, destructor)); +} + +void *MsanTSDGet() { + CHECK(tsd_key_inited); + return pthread_getspecific(tsd_key); +} + +void MsanTSDSet(void *tsd) { + CHECK(tsd_key_inited); + pthread_setspecific(tsd_key, tsd); +} + +void MsanTSDDtor(void *tsd) { + MsanThread *t = (MsanThread*)tsd; + if (t->destructor_iterations_ > 1) { + t->destructor_iterations_--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + MsanThread::TSDDtor(tsd); +} + } // namespace __msan -#endif // __linux__ +#endif // SANITIZER_FREEBSD || SANITIZER_LINUX diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc index 17687ddfc2130..9a8e56e4a28ad 100644 --- a/lib/msan/msan_new_delete.cc +++ b/lib/msan/msan_new_delete.cc @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "msan.h" +#include "interception/interception.h" #if MSAN_REPLACE_OPERATORS_NEW_AND_DELETE @@ -37,18 +38,26 @@ namespace std { GET_MALLOC_STACK_TRACE; \ return MsanReallocate(&stack, 0, size, sizeof(u64), false) +INTERCEPTOR_ATTRIBUTE void *operator new(size_t size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +INTERCEPTOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } #define OPERATOR_DELETE_BODY \ GET_MALLOC_STACK_TRACE; \ if (ptr) MsanDeallocate(&stack, ptr) -void operator delete(void *ptr) { OPERATOR_DELETE_BODY; } -void operator delete[](void *ptr) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete(void *ptr) throw() { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE +void operator delete[](void *ptr) throw() { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } diff --git a/lib/msan/msan_origin.h b/lib/msan/msan_origin.h new file mode 100644 index 0000000000000..1284c01bf2af4 --- /dev/null +++ b/lib/msan/msan_origin.h @@ -0,0 +1,169 @@ +//===-- msan_origin.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Origin id utils. +//===----------------------------------------------------------------------===// +#ifndef MSAN_ORIGIN_H +#define MSAN_ORIGIN_H + +#include "sanitizer_common/sanitizer_stackdepot.h" +#include "msan_chained_origin_depot.h" + +namespace __msan { + +// Origin handling. +// +// Origin is a 32-bit identifier that is attached to any uninitialized value in +// the program and describes, more or less exactly, how this memory came to be +// uninitialized. +// +// There are 3 kinds of origin ids: +// 1xxx xxxx xxxx xxxx heap origin id +// 0000 xxxx xxxx xxxx stack origin id +// 0zzz xxxx xxxx xxxx chained origin id +// +// Heap origin id describes a heap memory allocation and contains (in the xxx +// part) a value of StackDepot. +// +// Stack origin id describes a stack memory allocation and contains (in the xxx +// part) an index into StackOriginDescr and StackOriginPC. We don't store a +// stack trace for such origins for performance reasons. +// +// Chained origin id describes an event of storing an uninitialized value to +// memory. The xxx part is a value of ChainedOriginDepot, which is a mapping of +// (stack_id, prev_id) -> id, where +// * stack_id describes the event. +// StackDepot keeps a mapping between those and corresponding stack traces. +// * prev_id is another origin id that describes the earlier part of the +// uninitialized value history. +// Following a chain of prev_id provides the full recorded history of an +// uninitialized value. +// +// This, effectively, defines a tree (or 2 trees, see below) where nodes are +// points in value history marked with origin ids, and edges are events that are +// marked with stack_id. +// +// The "zzz" bits of chained origin id are used to store the length (or depth) +// of the origin chain. + +class Origin { + public: + static bool isValidId(u32 id) { return id != 0 && id != (u32)-1; } + + u32 raw_id() const { return raw_id_; } + bool isHeapOrigin() const { + // 1xxx xxxx xxxx xxxx + return raw_id_ >> kHeapShift == 0; + } + bool isStackOrigin() const { + // 1000 xxxx xxxx xxxx + return (raw_id_ >> kDepthShift) == (1 << kDepthBits); + } + bool isChainedOrigin() const { + // 1zzz xxxx xxxx xxxx, zzz != 000 + return (raw_id_ >> kDepthShift) > (1 << kDepthBits); + } + u32 getChainedId() const { + CHECK(isChainedOrigin()); + return raw_id_ & kChainedIdMask; + } + u32 getStackId() const { + CHECK(isStackOrigin()); + return raw_id_ & kChainedIdMask; + } + u32 getHeapId() const { + CHECK(isHeapOrigin()); + return raw_id_ & kHeapIdMask; + } + + // Returns the next origin in the chain and the current stack trace. + Origin getNextChainedOrigin(StackTrace *stack) const { + CHECK(isChainedOrigin()); + u32 prev_id; + u32 stack_id = ChainedOriginDepotGet(getChainedId(), &prev_id); + *stack = StackDepotGet(stack_id); + return Origin(prev_id); + } + + StackTrace getStackTraceForHeapOrigin() const { + return StackDepotGet(getHeapId()); + } + + static Origin CreateStackOrigin(u32 id) { + CHECK((id & kStackIdMask) == id); + return Origin((1 << kHeapShift) | id); + } + + static Origin CreateHeapOrigin(StackTrace *stack) { + u32 stack_id = StackDepotPut(*stack); + CHECK(stack_id); + CHECK((stack_id & kHeapIdMask) == stack_id); + return Origin(stack_id); + } + + static Origin CreateChainedOrigin(Origin prev, StackTrace *stack) { + int depth = prev.isChainedOrigin() ? prev.depth() : 0; + // depth is the length of the chain minus 1. + // origin_history_size of 0 means unlimited depth. + if (flags()->origin_history_size > 0) { + if (depth + 1 >= flags()->origin_history_size) { + return prev; + } else { + ++depth; + CHECK(depth < (1 << kDepthBits)); + } + } + + StackDepotHandle h = StackDepotPut_WithHandle(*stack); + if (!h.valid()) return prev; + + if (flags()->origin_history_per_stack_limit > 0) { + int use_count = h.use_count(); + if (use_count > flags()->origin_history_per_stack_limit) return prev; + } + + u32 chained_id; + bool inserted = ChainedOriginDepotPut(h.id(), prev.raw_id(), &chained_id); + CHECK((chained_id & kChainedIdMask) == chained_id); + + if (inserted && flags()->origin_history_per_stack_limit > 0) + h.inc_use_count_unsafe(); + + return Origin((1 << kHeapShift) | (depth << kDepthShift) | chained_id); + } + + static Origin FromRawId(u32 id) { + return Origin(id); + } + + private: + static const int kDepthBits = 3; + static const int kDepthShift = 32 - kDepthBits - 1; + + static const int kHeapShift = 31; + static const u32 kChainedIdMask = ((u32)-1) >> (32 - kDepthShift); + static const u32 kStackIdMask = ((u32)-1) >> (32 - kDepthShift); + static const u32 kHeapIdMask = ((u32)-1) >> (32 - kHeapShift); + + u32 raw_id_; + + explicit Origin(u32 raw_id) : raw_id_(raw_id) {} + + int depth() const { + CHECK(isChainedOrigin()); + return (raw_id_ >> kDepthShift) & ((1 << kDepthBits) - 1); + } + + public: + static const int kMaxDepth = (1 << kDepthBits) - 1; +}; + +} // namespace __msan + +#endif // MSAN_ORIGIN_H diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc index e3ef99307940a..717c4a958c8f4 100644 --- a/lib/msan/msan_report.cc +++ b/lib/msan/msan_report.cc @@ -13,6 +13,8 @@ //===----------------------------------------------------------------------===// #include "msan.h" +#include "msan_chained_origin_depot.h" +#include "msan_origin.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" @@ -25,44 +27,57 @@ using namespace __sanitizer; namespace __msan { -class Decorator: private __sanitizer::AnsiColorDecorator { +class Decorator: public __sanitizer::SanitizerCommonDecorator { public: - Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } + Decorator() : SanitizerCommonDecorator() { } const char *Warning() { return Red(); } const char *Origin() { return Magenta(); } const char *Name() { return Green(); } const char *End() { return Default(); } }; -static void DescribeOrigin(u32 origin) { +static void DescribeStackOrigin(const char *so, uptr pc) { Decorator d; - if (common_flags()->verbosity) - Printf(" raw origin id: %d\n", origin); - uptr pc; - if (const char *so = GetOriginDescrIfStack(origin, &pc)) { - char* s = internal_strdup(so); - char* sep = internal_strchr(s, '@'); - CHECK(sep); - *sep = '\0'; - Printf("%s", d.Origin()); - Printf(" %sUninitialized value was created by an allocation of '%s%s%s'" - " in the stack frame of function '%s%s%s'%s\n", - d.Origin(), d.Name(), s, d.Origin(), d.Name(), - Symbolizer::Get()->Demangle(sep + 1), d.Origin(), d.End()); - InternalFree(s); - - if (pc) { - // For some reason function address in LLVM IR is 1 less then the address - // of the first instruction. - pc += 1; - StackTrace::PrintStack(&pc, 1); - } + char *s = internal_strdup(so); + char *sep = internal_strchr(s, '@'); + CHECK(sep); + *sep = '\0'; + Printf("%s", d.Origin()); + Printf( + " %sUninitialized value was created by an allocation of '%s%s%s'" + " in the stack frame of function '%s%s%s'%s\n", + d.Origin(), d.Name(), s, d.Origin(), d.Name(), sep + 1, d.Origin(), + d.End()); + InternalFree(s); + + if (pc) { + // For some reason function address in LLVM IR is 1 less then the address + // of the first instruction. + pc = StackTrace::GetNextInstructionPc(pc); + StackTrace(&pc, 1).Print(); + } +} + +static void DescribeOrigin(u32 id) { + VPrintf(1, " raw origin id: %d\n", id); + Decorator d; + Origin o = Origin::FromRawId(id); + while (o.isChainedOrigin()) { + StackTrace stack; + o = o.getNextChainedOrigin(&stack); + Printf(" %sUninitialized value was stored to memory at%s\n", d.Origin(), + d.End()); + stack.Print(); + } + if (o.isStackOrigin()) { + uptr pc; + const char *so = GetStackOriginDescr(o.getStackId(), &pc); + DescribeStackOrigin(so, pc); } else { - uptr size = 0; - const uptr *trace = StackDepotGet(origin, &size); + StackTrace stack = o.getStackTraceForHeapOrigin(); Printf(" %sUninitialized value was created by a heap allocation%s\n", d.Origin(), d.End()); - StackTrace::PrintStack(trace, size); + stack.Print(); } } @@ -75,7 +90,7 @@ void ReportUMR(StackTrace *stack, u32 origin) { Printf("%s", d.Warning()); Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); Printf("%s", d.End()); - StackTrace::PrintStack(stack->trace, stack->size); + stack->Print(); if (origin) { DescribeOrigin(origin); } @@ -86,16 +101,162 @@ void ReportExpectedUMRNotFound(StackTrace *stack) { SpinMutexLock l(&CommonSanitizerReportMutex); Printf(" WARNING: Expected use of uninitialized value not found\n"); - StackTrace::PrintStack(stack->trace, stack->size); + stack->Print(); +} + +void ReportStats() { + SpinMutexLock l(&CommonSanitizerReportMutex); + + if (__msan_get_track_origins() > 0) { + StackDepotStats *stack_depot_stats = StackDepotGetStats(); + // FIXME: we want this at normal exit, too! + // FIXME: but only with verbosity=1 or something + Printf("Unique heap origins: %zu\n", stack_depot_stats->n_uniq_ids); + Printf("Stack depot allocated bytes: %zu\n", stack_depot_stats->allocated); + + StackDepotStats *chained_origin_depot_stats = ChainedOriginDepotGetStats(); + Printf("Unique origin histories: %zu\n", + chained_origin_depot_stats->n_uniq_ids); + Printf("History depot allocated bytes: %zu\n", + chained_origin_depot_stats->allocated); + } } void ReportAtExitStatistics() { SpinMutexLock l(&CommonSanitizerReportMutex); + if (msan_report_count > 0) { + Decorator d; + Printf("%s", d.Warning()); + Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); + Printf("%s", d.End()); + } +} + +class OriginSet { + public: + OriginSet() : next_id_(0) {} + int insert(u32 o) { + // Scan from the end for better locality. + for (int i = next_id_ - 1; i >= 0; --i) + if (origins_[i] == o) return i; + if (next_id_ == kMaxSize_) return OVERFLOW; + int id = next_id_++; + origins_[id] = o; + return id; + } + int size() { return next_id_; } + u32 get(int id) { return origins_[id]; } + static char asChar(int id) { + switch (id) { + case MISSING: + return '.'; + case OVERFLOW: + return '*'; + default: + return 'A' + id; + } + } + static const int OVERFLOW = -1; + static const int MISSING = -2; + + private: + static const int kMaxSize_ = 'Z' - 'A' + 1; + u32 origins_[kMaxSize_]; + int next_id_; +}; + +void DescribeMemoryRange(const void *x, uptr size) { + // Real limits. + uptr start = MEM_TO_SHADOW(x); + uptr end = start + size; + // Scan limits: align start down to 4; align size up to 16. + uptr s = start & ~3UL; + size = end - s; + size = (size + 15) & ~15UL; + uptr e = s + size; + + // Single letter names to origin id mapping. + OriginSet origin_set; + + uptr pos = 0; // Offset from aligned start. + bool with_origins = __msan_get_track_origins(); + // True if there is at least 1 poisoned bit in the last 4-byte group. + bool last_quad_poisoned; + int origin_ids[4]; // Single letter origin ids for the current line. + Decorator d; Printf("%s", d.Warning()); - Printf("MemorySanitizer: %d warnings reported.\n", msan_report_count); + Printf("Shadow map of [%p, %p), %zu bytes:\n", start, end, end - start); Printf("%s", d.End()); + while (s < e) { + // Line start. + if (pos % 16 == 0) { + for (int i = 0; i < 4; ++i) origin_ids[i] = -1; + Printf("%p:", s); + } + // Group start. + if (pos % 4 == 0) { + Printf(" "); + last_quad_poisoned = false; + } + // Print shadow byte. + if (s < start || s >= end) { + Printf(".."); + } else { + unsigned char v = *(unsigned char *)s; + if (v) last_quad_poisoned = true; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + Printf("%x%x", v & 0xf, v >> 4); +#else + Printf("%x%x", v >> 4, v & 0xf); +#endif + } + // Group end. + if (pos % 4 == 3 && with_origins) { + int id = OriginSet::MISSING; + if (last_quad_poisoned) { + u32 o = *(u32 *)SHADOW_TO_ORIGIN(s - 3); + id = origin_set.insert(o); + } + origin_ids[(pos % 16) / 4] = id; + } + // Line end. + if (pos % 16 == 15) { + if (with_origins) { + Printf(" |"); + for (int i = 0; i < 4; ++i) { + char c = OriginSet::asChar(origin_ids[i]); + Printf("%c", c); + if (i != 3) Printf(" "); + } + Printf("|"); + } + Printf("\n"); + } + size--; + s++; + pos++; + } + + Printf("\n"); + + for (int i = 0; i < origin_set.size(); ++i) { + u32 o = origin_set.get(i); + Printf("Origin %c (origin_id %x):\n", OriginSet::asChar(i), o); + DescribeOrigin(o); + } +} + +void ReportUMRInsideAddressRange(const char *what, const void *start, uptr size, + uptr offset) { + Decorator d; + Printf("%s", d.Warning()); + Printf("%sUninitialized bytes in %s%s%s at offset %zu inside [%p, %zu)%s\n", + d.Warning(), d.Name(), what, d.Warning(), offset, start, size, + d.End()); + if (__sanitizer::common_flags()->verbosity > 0) + DescribeMemoryRange(start, size); } } // namespace __msan diff --git a/lib/msan/msan_thread.cc b/lib/msan/msan_thread.cc new file mode 100644 index 0000000000000..f29a4b053a367 --- /dev/null +++ b/lib/msan/msan_thread.cc @@ -0,0 +1,93 @@ + +#include "msan.h" +#include "msan_thread.h" +#include "msan_interface_internal.h" + +#include "sanitizer_common/sanitizer_tls_get_addr.h" + +namespace __msan { + +MsanThread *MsanThread::Create(thread_callback_t start_routine, + void *arg) { + uptr PageSize = GetPageSizeCached(); + uptr size = RoundUpTo(sizeof(MsanThread), PageSize); + MsanThread *thread = (MsanThread*)MmapOrDie(size, __func__); + thread->start_routine_ = start_routine; + thread->arg_ = arg; + thread->destructor_iterations_ = kPthreadDestructorIterations; + + return thread; +} + +void MsanThread::SetThreadStackAndTls() { + uptr tls_size = 0; + uptr stack_size = 0; + GetThreadStackAndTls(IsMainThread(), &stack_bottom_, &stack_size, + &tls_begin_, &tls_size); + stack_top_ = stack_bottom_ + stack_size; + tls_end_ = tls_begin_ + tls_size; + + int local; + CHECK(AddrIsInStack((uptr)&local)); +} + +void MsanThread::ClearShadowForThreadStackAndTLS() { + __msan_unpoison((void *)stack_bottom_, stack_top_ - stack_bottom_); + if (tls_begin_ != tls_end_) + __msan_unpoison((void *)tls_begin_, tls_end_ - tls_begin_); + DTLS *dtls = DTLS_Get(); + CHECK_NE(dtls, 0); + for (uptr i = 0; i < dtls->dtv_size; ++i) + __msan_unpoison((void *)(dtls->dtv[i].beg), dtls->dtv[i].size); +} + +void MsanThread::Init() { + SetThreadStackAndTls(); + CHECK(MEM_IS_APP(stack_bottom_)); + CHECK(MEM_IS_APP(stack_top_ - 1)); + ClearShadowForThreadStackAndTLS(); +} + +void MsanThread::TSDDtor(void *tsd) { + MsanThread *t = (MsanThread*)tsd; + t->Destroy(); +} + +void MsanThread::Destroy() { + malloc_storage().CommitBack(); + // We also clear the shadow on thread destruction because + // some code may still be executing in later TSD destructors + // and we don't want it to have any poisoned stack. + ClearShadowForThreadStackAndTLS(); + uptr size = RoundUpTo(sizeof(MsanThread), GetPageSizeCached()); + UnmapOrDie(this, size); + DTLS_Destroy(); +} + +thread_return_t MsanThread::ThreadStart() { + Init(); + + if (!start_routine_) { + // start_routine_ == 0 if we're on the main thread or on one of the + // OS X libdispatch worker threads. But nobody is supposed to call + // ThreadStart() for the worker threads. + return 0; + } + + thread_return_t res = start_routine_(arg_); + + return res; +} + +MsanThread *GetCurrentThread() { + return reinterpret_cast<MsanThread *>(MsanTSDGet()); +} + +void SetCurrentThread(MsanThread *t) { + // Make sure we do not reset the current MsanThread. + CHECK_EQ(0, MsanTSDGet()); + MsanTSDSet(t); + CHECK_EQ(t, MsanTSDGet()); +} + +} // namespace __msan diff --git a/lib/msan/msan_thread.h b/lib/msan/msan_thread.h new file mode 100644 index 0000000000000..bc605b89a505f --- /dev/null +++ b/lib/msan/msan_thread.h @@ -0,0 +1,71 @@ +//===-- msan_thread.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of MemorySanitizer. +// +//===----------------------------------------------------------------------===// + +#ifndef MSAN_THREAD_H +#define MSAN_THREAD_H + +#include "msan_allocator.h" +#include "sanitizer_common/sanitizer_common.h" + +namespace __msan { + +class MsanThread { + public: + static MsanThread *Create(thread_callback_t start_routine, void *arg); + static void TSDDtor(void *tsd); + void Destroy(); + + void Init(); // Should be called from the thread itself. + thread_return_t ThreadStart(); + + uptr stack_top() { return stack_top_; } + uptr stack_bottom() { return stack_bottom_; } + uptr tls_begin() { return tls_begin_; } + uptr tls_end() { return tls_end_; } + bool IsMainThread() { return start_routine_ == 0; } + + bool AddrIsInStack(uptr addr) { + return addr >= stack_bottom_ && addr < stack_top_; + } + + bool InSignalHandler() { return in_signal_handler_; } + void EnterSignalHandler() { in_signal_handler_++; } + void LeaveSignalHandler() { in_signal_handler_--; } + + MsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } + + int destructor_iterations_; + + private: + // NOTE: There is no MsanThread constructor. It is allocated + // via mmap() and *must* be valid in zero-initialized state. + void SetThreadStackAndTls(); + void ClearShadowForThreadStackAndTLS(); + thread_callback_t start_routine_; + void *arg_; + uptr stack_top_; + uptr stack_bottom_; + uptr tls_begin_; + uptr tls_end_; + + unsigned in_signal_handler_; + + MsanThreadLocalMallocStorage malloc_storage_; +}; + +MsanThread *GetCurrentThread(); +void SetCurrentThread(MsanThread *t); + +} // namespace __msan + +#endif // MSAN_THREAD_H diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt index 9c49f167fa19e..53e1b549b70d7 100644 --- a/lib/msan/tests/CMakeLists.txt +++ b/lib/msan/tests/CMakeLists.txt @@ -5,46 +5,24 @@ include(CompilerRTLink) include_directories(..) include_directories(../..) -# Instrumented libcxx sources and build flags. -file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp) set(MSAN_LIBCXX_CFLAGS - -I${MSAN_LIBCXX_PATH}/include -fsanitize=memory -fsanitize-memory-track-origins - -fPIC - -Wno-\#warnings - -g - -O2 - -std=c++0x - -fstrict-aliasing - -fno-exceptions - -nostdinc++ - -fno-omit-frame-pointer - -mno-omit-leaf-frame-pointer) -set(MSAN_LIBCXX_LINK_FLAGS - -nodefaultlibs - -lpthread - -lrt - -lc - -lstdc++ - -fsanitize=memory) + -Wno-pedantic) # Unittest sources and build flags. set(MSAN_UNITTEST_SOURCES msan_test.cc msan_test_main.cc) set(MSAN_LOADABLE_SOURCE msan_loadable.cc) set(MSAN_UNITTEST_HEADERS msan_test_config.h - msandr_test_so.h ../../../include/sanitizer/msan_interface.h ) -set(MSANDR_UNITTEST_SOURCE msandr_test_so.cc) set(MSAN_UNITTEST_COMMON_CFLAGS - -I${MSAN_LIBCXX_PATH}/include - ${COMPILER_RT_GTEST_INCLUDE_CFLAGS} + -I${COMPILER_RT_LIBCXX_PATH}/include + ${COMPILER_RT_GTEST_CFLAGS} -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/msan - -std=c++0x -stdlib=libc++ -g -O2 @@ -52,6 +30,9 @@ set(MSAN_UNITTEST_COMMON_CFLAGS -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -Wno-deprecated-declarations + -Wno-unused-variable + -Wno-zero-length-array + -Werror=sign-compare ) set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS ${MSAN_UNITTEST_COMMON_CFLAGS} @@ -61,10 +42,11 @@ set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS ) set(MSAN_UNITTEST_LINK_FLAGS -fsanitize=memory - -ldl # FIXME: we build libcxx without cxxabi and need libstdc++ to provide it. -lstdc++ ) + +append_list_if(COMPILER_RT_HAS_LIBDL -ldl MSAN_UNITTEST_LINK_FLAGS) set(MSAN_LOADABLE_LINK_FLAGS -fsanitize=memory -shared @@ -72,20 +54,27 @@ set(MSAN_LOADABLE_LINK_FLAGS # Compile source for the given architecture, using compiler # options in ${ARGN}, and add it to the object list. -macro(msan_compile obj_list source arch) +macro(msan_compile obj_list source arch kind) get_filename_component(basename ${source} NAME) - set(output_obj "${basename}.${arch}.o") + set(output_obj "${basename}.${arch}${kind}.o") get_target_flags_for_arch(${arch} TARGET_CFLAGS) + set(COMPILE_DEPS ${MSAN_UNITTEST_HEADERS}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND COMPILE_DEPS gtest msan) + endif() clang_compile(${output_obj} ${source} CFLAGS ${ARGN} ${TARGET_CFLAGS} - DEPS gtest ${MSAN_RUNTIME_LIBRARIES} ${MSAN_UNITTEST_HEADERS}) + DEPS ${COMPILE_DEPS}) list(APPEND ${obj_list} ${output_obj}) endmacro() -macro(msan_link_shared so_list so_name arch) +macro(msan_link_shared so_list so_name arch kind) parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN}) - set(output_so "${CMAKE_CURRENT_BINARY_DIR}/${so_name}.${arch}.so") + set(output_so "${CMAKE_CURRENT_BINARY_DIR}/${so_name}.${arch}${kind}.so") get_target_flags_for_arch(${arch} TARGET_LINKFLAGS) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND SOURCE_DEPS msan) + endif() clang_link_shared(${output_so} OBJECTS ${SOURCE_OBJECTS} LINKFLAGS ${TARGET_LINKFLAGS} ${SOURCE_LINKFLAGS} @@ -93,80 +82,62 @@ macro(msan_link_shared so_list so_name arch) list(APPEND ${so_list} ${output_so}) endmacro() -# Link MSan unit test for a given architecture from a set -# of objects in ${ARGN}. -macro(add_msan_test test_suite test_name arch) - get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) - add_compiler_rt_test(${test_suite} ${test_name} - OBJECTS ${ARGN} - DEPS ${MSAN_RUNTIME_LIBRARIES} ${ARGN} - ${MSAN_LOADABLE_SO} - LINK_FLAGS ${MSAN_UNITTEST_LINK_FLAGS} - ${TARGET_LINK_FLAGS} - "-Wl,-rpath=${CMAKE_CURRENT_BINARY_DIR}") -endmacro() - # Main MemorySanitizer unit tests. add_custom_target(MsanUnitTests) set_target_properties(MsanUnitTests PROPERTIES FOLDER "MSan unit tests") # Adds MSan unit tests and benchmarks for architecture. -macro(add_msan_tests_for_arch arch) +macro(add_msan_tests_for_arch arch kind) + set(LIBCXX_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/../libcxx_msan${kind}) + add_custom_libcxx(libcxx_msan${kind} ${LIBCXX_PREFIX} + DEPS ${MSAN_RUNTIME_LIBRARIES} + CFLAGS ${MSAN_LIBCXX_CFLAGS} ${ARGN}) + set(MSAN_LIBCXX_SO ${LIBCXX_PREFIX}/lib/libc++.so) + # Build gtest instrumented with MSan. set(MSAN_INST_GTEST) - msan_compile(MSAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch} - ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) - - # Build libcxx instrumented with MSan. - set(MSAN_INST_LIBCXX_OBJECTS) - foreach(SOURCE ${MSAN_LIBCXX_SOURCES}) - msan_compile(MSAN_INST_LIBCXX_OBJECTS ${SOURCE} ${arch} - ${MSAN_LIBCXX_CFLAGS}) - endforeach(SOURCE) - - set(MSAN_INST_LIBCXX) - msan_link_shared(MSAN_INST_LIBCXX "libcxx" ${arch} - OBJECTS ${MSAN_INST_LIBCXX_OBJECTS} - LINKFLAGS ${MSAN_LIBCXX_LINK_FLAGS} - DEPS ${MSAN_INST_LIBCXX_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) + msan_compile(MSAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch} "${kind}" + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) # Instrumented tests. set(MSAN_INST_TEST_OBJECTS) foreach (SOURCE ${MSAN_UNITTEST_SOURCES}) - msan_compile(MSAN_INST_TEST_OBJECTS ${SOURCE} ${arch} - ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) + msan_compile(MSAN_INST_TEST_OBJECTS ${SOURCE} ${arch} "${kind}" + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) endforeach(SOURCE) # Instrumented loadable module objects. set(MSAN_INST_LOADABLE_OBJECTS) - msan_compile(MSAN_INST_LOADABLE_OBJECTS ${MSAN_LOADABLE_SOURCE} ${arch} - ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS}) - - # Uninstrumented shared object for MSanDR tests. - set(MSANDR_TEST_OBJECTS) - msan_compile(MSANDR_TEST_OBJECTS ${MSANDR_UNITTEST_SOURCE} ${arch} - ${MSAN_UNITTEST_COMMON_CFLAGS}) + msan_compile(MSAN_INST_LOADABLE_OBJECTS ${MSAN_LOADABLE_SOURCE} ${arch} "${kind}" + ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS} ${ARGN}) # Instrumented loadable library tests. set(MSAN_LOADABLE_SO) - msan_link_shared(MSAN_LOADABLE_SO "libmsan_loadable" ${arch} + msan_link_shared(MSAN_LOADABLE_SO "libmsan_loadable" ${arch} "${kind}" OBJECTS ${MSAN_INST_LOADABLE_OBJECTS} - DEPS ${MSAN_INST_LOADABLE_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) - - # Uninstrumented shared library tests. - set(MSANDR_TEST_SO) - msan_link_shared(MSANDR_TEST_SO "libmsandr_test" ${arch} - OBJECTS ${MSANDR_TEST_OBJECTS} - DEPS ${MSANDR_TEST_OBJECTS} ${MSAN_RUNTIME_LIBRARIES}) + DEPS ${MSAN_INST_LOADABLE_OBJECTS}) - # Link everything together. - add_msan_test(MsanUnitTests "Msan-${arch}-Test" ${arch} - ${MSAN_INST_TEST_OBJECTS} ${MSAN_INST_GTEST} - ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO}) + set(MSAN_TEST_OBJECTS ${MSAN_INST_TEST_OBJECTS} ${MSAN_INST_GTEST}) + set(MSAN_TEST_DEPS ${MSAN_TEST_OBJECTS} libcxx_msan${kind} + ${MSAN_LOADABLE_SO}) + if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND MSAN_TEST_DEPS msan) + endif() + get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) + add_compiler_rt_test(MsanUnitTests "Msan-${arch}${kind}-Test" ${arch} + OBJECTS ${MSAN_TEST_OBJECTS} ${MSAN_LIBCXX_SO} + DEPS ${MSAN_TEST_DEPS} + LINK_FLAGS ${MSAN_UNITTEST_LINK_FLAGS} + ${TARGET_LINK_FLAGS} + "-Wl,-rpath=${CMAKE_CURRENT_BINARY_DIR}" + "-Wl,-rpath=${LIBCXX_PREFIX}/lib") endmacro() -if(COMPILER_RT_CAN_EXECUTE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) +# We should only build MSan unit tests if we can build instrumented libcxx. +if(COMPILER_RT_CAN_EXECUTE_TESTS AND COMPILER_RT_HAS_LIBCXX_SOURCES) if(CAN_TARGET_x86_64) - add_msan_tests_for_arch(x86_64) + add_msan_tests_for_arch(x86_64 "") + add_msan_tests_for_arch(x86_64 "-with-call" + -mllvm -msan-instrumentation-with-call-threshold=0) endif() endif() diff --git a/lib/msan/tests/msan_loadable.cc b/lib/msan/tests/msan_loadable.cc index db3bf489853dc..06e880f90dee1 100644 --- a/lib/msan/tests/msan_loadable.cc +++ b/lib/msan/tests/msan_loadable.cc @@ -20,24 +20,6 @@ static void *dso_global; // No name mangling. extern "C" { -__attribute__((constructor)) -void loadable_module_init(void) { - if (!__msan_has_dynamic_component()) - return; - // The real test is that this compare should not make an uninit. - if (dso_global == NULL) - dso_global = malloc(4); -} - -__attribute__((destructor)) -void loadable_module_fini(void) { - if (!__msan_has_dynamic_component()) - return; - free(dso_global); - // *Don't* overwrite it with NULL! That would unpoison it, but our test - // relies on reloading at the same address and keeping the poison. -} - void **get_dso_global() { return &dso_global; } diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index f95bb4e7c618b..554265da6aa97 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -16,14 +16,15 @@ #include "msan_test_config.h" #endif // MSAN_EXTERNAL_TEST_CONFIG +#include "sanitizer_common/tests/sanitizer_test_utils.h" + +#include "sanitizer/allocator_interface.h" #include "sanitizer/msan_interface.h" -#include "msandr_test_so.h" #include <inttypes.h> #include <stdlib.h> #include <stdarg.h> #include <stdio.h> -#include <assert.h> #include <wchar.h> #include <math.h> #include <malloc.h> @@ -63,7 +64,11 @@ # define MSAN_HAS_M128 0 #endif -static const int kPageSize = 4096; +#ifdef __AVX2__ +# include <immintrin.h> +#endif + +static const size_t kPageSize = 4096; typedef unsigned char U1; typedef unsigned short U2; // NOLINT @@ -100,20 +105,6 @@ static bool TrackingOrigins() { EXPECT_EQ(origin, __msan_get_umr_origin()); \ } while (0) -#define EXPECT_UMR_S(action, stack_origin) \ - do { \ - __msan_set_expect_umr(1); \ - action; \ - __msan_set_expect_umr(0); \ - U4 id = __msan_get_umr_origin(); \ - const char *str = __msan_get_origin_descr_if_stack(id); \ - if (!str || strcmp(str, stack_origin)) { \ - fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", \ - id, stack_origin, str); \ - EXPECT_EQ(1, 0); \ - } \ - } while (0) - #define EXPECT_POISONED(x) ExpectPoisoned(x) template<typename T> @@ -131,21 +122,6 @@ void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { EXPECT_EQ(origin, __msan_get_origin((void*)&t)); } -#define EXPECT_POISONED_S(x, stack_origin) \ - ExpectPoisonedWithStackOrigin(x, stack_origin) - -template<typename T> -void ExpectPoisonedWithStackOrigin(const T& t, const char *stack_origin) { - EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); - U4 id = __msan_get_origin((void*)&t); - const char *str = __msan_get_origin_descr_if_stack(id); - if (!str || strcmp(str, stack_origin)) { - fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", - id, stack_origin, str); - EXPECT_EQ(1, 0); - } -} - #define EXPECT_NOT_POISONED(x) ExpectNotPoisoned(x) template<typename T> @@ -171,13 +147,10 @@ T *GetPoisonedO(int i, U4 origin, T val = 0) { return res; } -// This function returns its parameter but in such a way that compiler -// can not prove it. -template<class T> -NOINLINE -static T Ident(T t) { - volatile T ret = t; - return ret; +template<typename T> +T Poisoned(T v = 0, T s = (T)(-1)) { + __msan_partial_poison(&v, &s, sizeof(T)); + return v; } template<class T> NOINLINE T ReturnPoisoned() { return *GetPoisoned<T>(); } @@ -277,7 +250,6 @@ TEST(MemorySanitizer, ArgTest) { TEST(MemorySanitizer, CallAndRet) { - if (!__msan_has_dynamic_component()) return; ReturnPoisoned<S1>(); ReturnPoisoned<S2>(); ReturnPoisoned<S4>(); @@ -327,10 +299,27 @@ TEST(MemorySanitizer, Realloc) { TEST(MemorySanitizer, Calloc) { S4 *x = (int*)Ident(calloc(1, sizeof(S4))); EXPECT_NOT_POISONED(*x); // Should not be poisoned. - // EXPECT_EQ(0, *x); + EXPECT_EQ(0, *x); free(x); } +TEST(MemorySanitizer, CallocReturnsZeroMem) { + size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; + for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { + size_t size = sizes[s]; + for (size_t iter = 0; iter < 5; iter++) { + char *x = Ident((char*)calloc(1, size)); + EXPECT_EQ(x[0], 0); + EXPECT_EQ(x[size - 1], 0); + EXPECT_EQ(x[size / 2], 0); + EXPECT_EQ(x[size / 3], 0); + EXPECT_EQ(x[size / 4], 0); + memset(x, 0x42, size); + free(Ident(x)); + } + } +} + TEST(MemorySanitizer, AndOr) { U4 *p = GetPoisoned<U4>(); // We poison two bytes in the midle of a 4-byte word to make the test @@ -503,14 +492,12 @@ TEST(MemorySanitizer, DynMem) { static char *DynRetTestStr; TEST(MemorySanitizer, DynRet) { - if (!__msan_has_dynamic_component()) return; ReturnPoisoned<S8>(); EXPECT_NOT_POISONED(clearenv()); } TEST(MemorySanitizer, DynRet1) { - if (!__msan_has_dynamic_component()) return; ReturnPoisoned<S8>(); } @@ -573,39 +560,39 @@ TEST(MemorySanitizer, strerror_r) { TEST(MemorySanitizer, fread) { char *x = new char[32]; FILE *f = fopen("/proc/self/stat", "r"); - assert(f); + ASSERT_TRUE(f != NULL); fread(x, 1, 32, f); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); fclose(f); - delete x; + delete[] x; } TEST(MemorySanitizer, read) { char *x = new char[32]; int fd = open("/proc/self/stat", O_RDONLY); - assert(fd > 0); + ASSERT_GT(fd, 0); int sz = read(fd, x, 32); - assert(sz == 32); + ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); - delete x; + delete[] x; } TEST(MemorySanitizer, pread) { char *x = new char[32]; int fd = open("/proc/self/stat", O_RDONLY); - assert(fd > 0); + ASSERT_GT(fd, 0); int sz = pread(fd, x, 32, 0); - assert(sz == 32); + ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); - delete x; + delete[] x; } TEST(MemorySanitizer, readv) { @@ -616,10 +603,11 @@ TEST(MemorySanitizer, readv) { iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; int fd = open("/proc/self/stat", O_RDONLY); - assert(fd > 0); + ASSERT_GT(fd, 0); int sz = readv(fd, iov, 2); + ASSERT_GE(sz, 0); ASSERT_LT(sz, 5 + 2000); - ASSERT_GT(sz, iov[0].iov_len); + ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); @@ -639,10 +627,11 @@ TEST(MemorySanitizer, preadv) { iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; int fd = open("/proc/self/stat", O_RDONLY); - assert(fd > 0); + ASSERT_GT(fd, 0); int sz = preadv(fd, iov, 2, 3); + ASSERT_GE(sz, 0); ASSERT_LT(sz, 5 + 2000); - ASSERT_GT(sz, iov[0].iov_len); + ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); @@ -672,7 +661,7 @@ TEST(MemorySanitizer, readlink) { TEST(MemorySanitizer, stat) { struct stat* st = new struct stat; int res = stat("/proc/self/stat", st); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); @@ -681,9 +670,9 @@ TEST(MemorySanitizer, stat) { TEST(MemorySanitizer, fstatat) { struct stat* st = new struct stat; int dirfd = open("/proc/self", O_RDONLY); - assert(dirfd > 0); + ASSERT_GT(dirfd, 0); int res = fstatat(dirfd, "stat", st, 0); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); @@ -693,7 +682,7 @@ TEST(MemorySanitizer, fstatat) { TEST(MemorySanitizer, statfs) { struct statfs st; int res = statfs("/", &st); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_type); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namelen); @@ -702,7 +691,7 @@ TEST(MemorySanitizer, statfs) { TEST(MemorySanitizer, statvfs) { struct statvfs st; int res = statvfs("/", &st); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); @@ -713,7 +702,7 @@ TEST(MemorySanitizer, fstatvfs) { struct statvfs st; int fd = open("/", O_RDONLY | O_DIRECTORY); int res = fstatvfs(fd, &st); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); @@ -724,7 +713,7 @@ TEST(MemorySanitizer, fstatvfs) { TEST(MemorySanitizer, pipe) { int* pipefd = new int[2]; int res = pipe(pipefd); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); @@ -734,7 +723,7 @@ TEST(MemorySanitizer, pipe) { TEST(MemorySanitizer, pipe2) { int* pipefd = new int[2]; int res = pipe2(pipefd, O_NONBLOCK); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); @@ -744,7 +733,7 @@ TEST(MemorySanitizer, pipe2) { TEST(MemorySanitizer, socketpair) { int sv[2]; int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(sv[0]); EXPECT_NOT_POISONED(sv[1]); close(sv[0]); @@ -823,7 +812,7 @@ TEST(MemorySanitizer, bind_getsockname) { sai.sin_family = AF_UNIX; int res = bind(sock, (struct sockaddr *)&sai, sizeof(sai)); - assert(!res); + ASSERT_EQ(0, res); char buf[200]; socklen_t addrlen; EXPECT_UMR(getsockname(sock, (struct sockaddr *)&buf, &addrlen)); @@ -908,10 +897,10 @@ TEST(MemorySanitizer, getnameinfo) { EXPECT_NOT_POISONED(host[0]); EXPECT_POISONED(host[sizeof(host) - 1]); - ASSERT_NE(0, strlen(host)); + ASSERT_NE(0U, strlen(host)); EXPECT_NOT_POISONED(serv[0]); EXPECT_POISONED(serv[sizeof(serv) - 1]); - ASSERT_NE(0, strlen(serv)); + ASSERT_NE(0U, strlen(serv)); } #define EXPECT_HOSTENT_NOT_POISONED(he) \ @@ -1061,6 +1050,26 @@ TEST(MemorySanitizer, gethostbyname_r) { EXPECT_NOT_POISONED(err); } +TEST(MemorySanitizer, gethostbyname_r_bad_host_name) { + char buf[2000]; + struct hostent he; + struct hostent *result; + int err; + int res = gethostbyname_r("bad-host-name", &he, buf, sizeof(buf), &result, &err); + ASSERT_EQ((struct hostent *)0, result); + EXPECT_NOT_POISONED(err); +} + +TEST(MemorySanitizer, gethostbyname_r_erange) { + char buf[5]; + struct hostent he; + struct hostent *result; + int err; + int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); + ASSERT_EQ(ERANGE, res); + EXPECT_NOT_POISONED(err); +} + TEST(MemorySanitizer, gethostbyname2_r) { char buf[2000]; struct hostent he; @@ -1105,20 +1114,20 @@ TEST(MemorySanitizer, getsockopt) { TEST(MemorySanitizer, getcwd) { char path[PATH_MAX + 1]; char* res = getcwd(path, sizeof(path)); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } TEST(MemorySanitizer, getcwd_gnu) { char* res = getcwd(NULL, 0); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } TEST(MemorySanitizer, get_current_dir_name) { char* res = get_current_dir_name(); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } @@ -1209,7 +1218,7 @@ TEST(MemorySanitizer, confstr) { TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); - assert(d); + ASSERT_TRUE(d != NULL); EXPECT_NOT_POISONED(d->d_name[0]); closedir(dir); } @@ -1219,7 +1228,7 @@ TEST(MemorySanitizer, readdir_r) { struct dirent d; struct dirent *pd; int res = readdir_r(dir, &d, &pd); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pd); EXPECT_NOT_POISONED(d.d_name[0]); closedir(dir); @@ -1229,7 +1238,7 @@ TEST(MemorySanitizer, realpath) { const char* relpath = "."; char path[PATH_MAX + 1]; char* res = realpath(relpath, path); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } @@ -1237,7 +1246,7 @@ TEST(MemorySanitizer, realpath_null) { const char* relpath = "."; char* res = realpath(relpath, NULL); printf("%d, %s\n", errno, strerror(errno)); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } @@ -1245,7 +1254,7 @@ TEST(MemorySanitizer, realpath_null) { TEST(MemorySanitizer, canonicalize_file_name) { const char* relpath = "."; char* res = canonicalize_file_name(relpath); - assert(res); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } @@ -1279,33 +1288,56 @@ TEST(MemorySanitizer, memcpy) { EXPECT_POISONED(y[1]); } -void TestUnalignedMemcpy(int left, int right, bool src_is_aligned) { - const int sz = 20; +void TestUnalignedMemcpy(unsigned left, unsigned right, bool src_is_aligned, + bool src_is_poisoned, bool dst_is_poisoned) { + fprintf(stderr, "%s(%d, %d, %d, %d, %d)\n", __func__, left, right, + src_is_aligned, src_is_poisoned, dst_is_poisoned); + + const unsigned sz = 20; + U4 dst_origin, src_origin; char *dst = (char *)malloc(sz); - U4 origin = __msan_get_origin(dst); + if (dst_is_poisoned) + dst_origin = __msan_get_origin(dst); + else + memset(dst, 0, sz); char *src = (char *)malloc(sz); - memset(src, 0, sz); + if (src_is_poisoned) + src_origin = __msan_get_origin(src); + else + memset(src, 0, sz); memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); - for (int i = 0; i < left; ++i) - EXPECT_POISONED_O(dst[i], origin); - for (int i = 0; i < right; ++i) - EXPECT_POISONED_O(dst[sz - i - 1], origin); - EXPECT_NOT_POISONED(dst[left]); - EXPECT_NOT_POISONED(dst[sz - right - 1]); + + for (unsigned i = 0; i < (left & (~3U)); ++i) + if (dst_is_poisoned) + EXPECT_POISONED_O(dst[i], dst_origin); + else + EXPECT_NOT_POISONED(dst[i]); + + for (unsigned i = 0; i < (right & (~3U)); ++i) + if (dst_is_poisoned) + EXPECT_POISONED_O(dst[sz - i - 1], dst_origin); + else + EXPECT_NOT_POISONED(dst[sz - i - 1]); + + for (unsigned i = left; i < sz - right; ++i) + if (src_is_poisoned) + EXPECT_POISONED_O(dst[i], src_origin); + else + EXPECT_NOT_POISONED(dst[i]); free(dst); free(src); } TEST(MemorySanitizer, memcpy_unaligned) { - for (int i = 0; i < 10; ++i) { - for (int j = 0; j < 10; ++j) { - TestUnalignedMemcpy(i, j, true); - TestUnalignedMemcpy(i, j, false); - } - } + for (int i = 0; i < 10; ++i) + for (int j = 0; j < 10; ++j) + for (int aligned = 0; aligned < 2; ++aligned) + for (int srcp = 0; srcp < 2; ++srcp) + for (int dstp = 0; dstp < 2; ++dstp) + TestUnalignedMemcpy(i, j, aligned, srcp, dstp); } TEST(MemorySanitizer, memmove) { @@ -1412,17 +1444,12 @@ TEST(MemorySanitizer, strndup_short) { template<class T, int size> void TestOverlapMemmove() { T *x = new T[size]; - assert(size >= 3); + ASSERT_GE(size, 3); x[2] = 0; memmove(x, x + 1, (size - 1) * sizeof(T)); EXPECT_NOT_POISONED(x[1]); - if (!__msan_has_dynamic_component()) { - // FIXME: under DR we will lose this information - // because accesses in memmove will unpoisin the shadow. - // We need to use our own memove implementation instead of libc's. - EXPECT_POISONED(x[0]); - EXPECT_POISONED(x[2]); - } + EXPECT_POISONED(x[0]); + EXPECT_POISONED(x[2]); delete [] x; } @@ -1447,14 +1474,16 @@ TEST(MemorySanitizer, strcpy) { // NOLINT TEST(MemorySanitizer, strncpy) { // NOLINT char* x = new char[3]; - char* y = new char[3]; + char* y = new char[5]; x[0] = 'a'; x[1] = *GetPoisoned<char>(1, 1); - x[2] = 0; - strncpy(y, x, 2); // NOLINT + x[2] = '\0'; + strncpy(y, x, 4); // NOLINT EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); - EXPECT_POISONED(y[2]); + EXPECT_NOT_POISONED(y[2]); + EXPECT_NOT_POISONED(y[3]); + EXPECT_POISONED(y[4]); } TEST(MemorySanitizer, stpcpy) { // NOLINT @@ -1470,71 +1499,144 @@ TEST(MemorySanitizer, stpcpy) { // NOLINT EXPECT_NOT_POISONED(y[2]); } -TEST(MemorySanitizer, strtol) { - char *e; - assert(1 == strtol("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} +TEST(MemorySanitizer, strcat) { // NOLINT + char a[10]; + char b[] = "def"; + strcpy(a, "abc"); + __msan_poison(b + 1, 1); + strcat(a, b); + EXPECT_NOT_POISONED(a[3]); + EXPECT_POISONED(a[4]); + EXPECT_NOT_POISONED(a[5]); + EXPECT_NOT_POISONED(a[6]); + EXPECT_POISONED(a[7]); +} + +TEST(MemorySanitizer, strncat) { // NOLINT + char a[10]; + char b[] = "def"; + strcpy(a, "abc"); + __msan_poison(b + 1, 1); + strncat(a, b, 5); + EXPECT_NOT_POISONED(a[3]); + EXPECT_POISONED(a[4]); + EXPECT_NOT_POISONED(a[5]); + EXPECT_NOT_POISONED(a[6]); + EXPECT_POISONED(a[7]); +} + +TEST(MemorySanitizer, strncat_overflow) { // NOLINT + char a[10]; + char b[] = "def"; + strcpy(a, "abc"); + __msan_poison(b + 1, 1); + strncat(a, b, 2); + EXPECT_NOT_POISONED(a[3]); + EXPECT_POISONED(a[4]); + EXPECT_NOT_POISONED(a[5]); + EXPECT_POISONED(a[6]); + EXPECT_POISONED(a[7]); +} + +#define TEST_STRTO_INT(func_name, char_type, str_prefix) \ + TEST(MemorySanitizer, func_name) { \ + char_type *e; \ + EXPECT_EQ(1U, func_name(str_prefix##"1", &e, 10)); \ + EXPECT_NOT_POISONED((S8)e); \ + } -TEST(MemorySanitizer, strtoll) { - char *e; - assert(1 == strtoll("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} +#define TEST_STRTO_FLOAT(func_name, char_type, str_prefix) \ + TEST(MemorySanitizer, func_name) { \ + char_type *e; \ + EXPECT_NE(0, func_name(str_prefix##"1.5", &e)); \ + EXPECT_NOT_POISONED((S8)e); \ + } -TEST(MemorySanitizer, strtoul) { - char *e; - assert(1 == strtoul("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} +#define TEST_STRTO_FLOAT_LOC(func_name, char_type, str_prefix) \ + TEST(MemorySanitizer, func_name) { \ + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ + char_type *e; \ + EXPECT_NE(0, func_name(str_prefix##"1.5", &e, loc)); \ + EXPECT_NOT_POISONED((S8)e); \ + freelocale(loc); \ + } + +#define TEST_STRTO_INT_LOC(func_name, char_type, str_prefix) \ + TEST(MemorySanitizer, func_name) { \ + locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ + char_type *e; \ + ASSERT_EQ(1U, func_name(str_prefix##"1", &e, 10, loc)); \ + EXPECT_NOT_POISONED((S8)e); \ + freelocale(loc); \ + } + +TEST_STRTO_INT(strtol, char, ) +TEST_STRTO_INT(strtoll, char, ) +TEST_STRTO_INT(strtoul, char, ) +TEST_STRTO_INT(strtoull, char, ) + +TEST_STRTO_FLOAT(strtof, char, ) +TEST_STRTO_FLOAT(strtod, char, ) +TEST_STRTO_FLOAT(strtold, char, ) + +TEST_STRTO_FLOAT_LOC(strtof_l, char, ) +TEST_STRTO_FLOAT_LOC(strtod_l, char, ) +TEST_STRTO_FLOAT_LOC(strtold_l, char, ) + +TEST_STRTO_INT_LOC(strtol_l, char, ) +TEST_STRTO_INT_LOC(strtoll_l, char, ) +TEST_STRTO_INT_LOC(strtoul_l, char, ) +TEST_STRTO_INT_LOC(strtoull_l, char, ) + +TEST_STRTO_INT(wcstol, wchar_t, L) +TEST_STRTO_INT(wcstoll, wchar_t, L) +TEST_STRTO_INT(wcstoul, wchar_t, L) +TEST_STRTO_INT(wcstoull, wchar_t, L) + +TEST_STRTO_FLOAT(wcstof, wchar_t, L) +TEST_STRTO_FLOAT(wcstod, wchar_t, L) +TEST_STRTO_FLOAT(wcstold, wchar_t, L) + +TEST_STRTO_FLOAT_LOC(wcstof_l, wchar_t, L) +TEST_STRTO_FLOAT_LOC(wcstod_l, wchar_t, L) +TEST_STRTO_FLOAT_LOC(wcstold_l, wchar_t, L) + +TEST_STRTO_INT_LOC(wcstol_l, wchar_t, L) +TEST_STRTO_INT_LOC(wcstoll_l, wchar_t, L) +TEST_STRTO_INT_LOC(wcstoul_l, wchar_t, L) +TEST_STRTO_INT_LOC(wcstoull_l, wchar_t, L) -TEST(MemorySanitizer, strtoull) { - char *e; - assert(1 == strtoull("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} TEST(MemorySanitizer, strtoimax) { char *e; - assert(1 == strtoimax("1", &e, 10)); + ASSERT_EQ(1, strtoimax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } TEST(MemorySanitizer, strtoumax) { char *e; - assert(1 == strtoumax("1", &e, 10)); - EXPECT_NOT_POISONED((S8) e); -} - -TEST(MemorySanitizer, strtod) { - char *e; - assert(0 != strtod("1.5", &e)); + ASSERT_EQ(1U, strtoumax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } #ifdef __GLIBC__ +extern "C" float __strtof_l(const char *nptr, char **endptr, locale_t loc); +TEST_STRTO_FLOAT_LOC(__strtof_l, char, ) extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); -TEST(MemorySanitizer, __strtod_l) { - locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); - char *e; - assert(0 != __strtod_l("1.5", &e, loc)); - EXPECT_NOT_POISONED((S8) e); - freelocale(loc); -} +TEST_STRTO_FLOAT_LOC(__strtod_l, char, ) +extern "C" long double __strtold_l(const char *nptr, char **endptr, + locale_t loc); +TEST_STRTO_FLOAT_LOC(__strtold_l, char, ) + +extern "C" float __wcstof_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); +TEST_STRTO_FLOAT_LOC(__wcstof_l, wchar_t, L) +extern "C" double __wcstod_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); +TEST_STRTO_FLOAT_LOC(__wcstod_l, wchar_t, L) +extern "C" long double __wcstold_l(const wchar_t *nptr, wchar_t **endptr, + locale_t loc); +TEST_STRTO_FLOAT_LOC(__wcstold_l, wchar_t, L) #endif // __GLIBC__ -TEST(MemorySanitizer, strtof) { - char *e; - assert(0 != strtof("1.5", &e)); - EXPECT_NOT_POISONED((S8) e); -} - -TEST(MemorySanitizer, strtold) { - char *e; - assert(0 != strtold("1.5", &e)); - EXPECT_NOT_POISONED((S8) e); -} - TEST(MemorySanitizer, modf) { double x, y; x = modf(2.1, &y); @@ -1655,12 +1757,12 @@ TEST(MemorySanitizer, sprintf) { // NOLINT break_optimization(buff); EXPECT_POISONED(buff[0]); int res = sprintf(buff, "%d", 1234567); // NOLINT - assert(res == 7); - assert(buff[0] == '1'); - assert(buff[1] == '2'); - assert(buff[2] == '3'); - assert(buff[6] == '7'); - assert(buff[7] == 0); + ASSERT_EQ(res, 7); + ASSERT_EQ(buff[0], '1'); + ASSERT_EQ(buff[1], '2'); + ASSERT_EQ(buff[2], '3'); + ASSERT_EQ(buff[6], '7'); + ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } @@ -1669,27 +1771,27 @@ TEST(MemorySanitizer, snprintf) { break_optimization(buff); EXPECT_POISONED(buff[0]); int res = snprintf(buff, sizeof(buff), "%d", 1234567); - assert(res == 7); - assert(buff[0] == '1'); - assert(buff[1] == '2'); - assert(buff[2] == '3'); - assert(buff[6] == '7'); - assert(buff[7] == 0); + ASSERT_EQ(res, 7); + ASSERT_EQ(buff[0], '1'); + ASSERT_EQ(buff[1], '2'); + ASSERT_EQ(buff[2], '3'); + ASSERT_EQ(buff[6], '7'); + ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, swprintf) { wchar_t buff[10]; - assert(sizeof(wchar_t) == 4); + ASSERT_EQ(4U, sizeof(wchar_t)); break_optimization(buff); EXPECT_POISONED(buff[0]); int res = swprintf(buff, 9, L"%d", 1234567); - assert(res == 7); - assert(buff[0] == '1'); - assert(buff[1] == '2'); - assert(buff[2] == '3'); - assert(buff[6] == '7'); - assert(buff[7] == 0); + ASSERT_EQ(res, 7); + ASSERT_EQ(buff[0], '1'); + ASSERT_EQ(buff[1], '2'); + ASSERT_EQ(buff[2], '3'); + ASSERT_EQ(buff[6], '7'); + ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } @@ -1697,13 +1799,13 @@ TEST(MemorySanitizer, asprintf) { // NOLINT char *pbuf; EXPECT_POISONED(pbuf); int res = asprintf(&pbuf, "%d", 1234567); // NOLINT - assert(res == 7); + ASSERT_EQ(res, 7); EXPECT_NOT_POISONED(pbuf); - assert(pbuf[0] == '1'); - assert(pbuf[1] == '2'); - assert(pbuf[2] == '3'); - assert(pbuf[6] == '7'); - assert(pbuf[7] == 0); + ASSERT_EQ(pbuf[0], '1'); + ASSERT_EQ(pbuf[1], '2'); + ASSERT_EQ(pbuf[2], '3'); + ASSERT_EQ(pbuf[6], '7'); + ASSERT_EQ(pbuf[7], 0); free(pbuf); } @@ -1758,6 +1860,16 @@ TEST(MemorySanitizer, wcsnrtombs) { EXPECT_POISONED(buff[2]); } +TEST(MemorySanitizer, wmemset) { + wchar_t x[25]; + break_optimization(x); + EXPECT_POISONED(x[0]); + wmemset(x, L'A', 10); + EXPECT_EQ(x[0], L'A'); + EXPECT_EQ(x[9], L'A'); + EXPECT_POISONED(x[10]); +} + TEST(MemorySanitizer, mbtowc) { const char *x = "abc"; wchar_t wx; @@ -1776,18 +1888,29 @@ TEST(MemorySanitizer, mbrtowc) { EXPECT_NOT_POISONED(wx); } +TEST(MemorySanitizer, wcsftime) { + wchar_t x[100]; + time_t t = time(NULL); + struct tm tms; + struct tm *tmres = localtime_r(&t, &tms); + ASSERT_NE((void *)0, tmres); + size_t res = wcsftime(x, sizeof(x) / sizeof(x[0]), L"%Y-%m-%d", tmres); + EXPECT_GT(res, 0UL); + EXPECT_EQ(res, wcslen(x)); +} + TEST(MemorySanitizer, gettimeofday) { struct timeval tv; struct timezone tz; break_optimization(&tv); break_optimization(&tz); - assert(sizeof(tv) == 16); - assert(sizeof(tz) == 8); + ASSERT_EQ(16U, sizeof(tv)); + ASSERT_EQ(8U, sizeof(tz)); EXPECT_POISONED(tv.tv_sec); EXPECT_POISONED(tv.tv_usec); EXPECT_POISONED(tz.tz_minuteswest); EXPECT_POISONED(tz.tz_dsttime); - assert(0 == gettimeofday(&tv, &tz)); + ASSERT_EQ(0, gettimeofday(&tv, &tz)); EXPECT_NOT_POISONED(tv.tv_sec); EXPECT_NOT_POISONED(tv.tv_usec); EXPECT_NOT_POISONED(tz.tz_minuteswest); @@ -1798,7 +1921,7 @@ TEST(MemorySanitizer, clock_gettime) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); - assert(0 == clock_gettime(CLOCK_REALTIME, &tp)); + ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } @@ -1807,10 +1930,10 @@ TEST(MemorySanitizer, clock_getres) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); - assert(0 == clock_getres(CLOCK_REALTIME, 0)); + ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, 0)); EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); - assert(0 == clock_getres(CLOCK_REALTIME, &tp)); + ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } @@ -1823,7 +1946,7 @@ TEST(MemorySanitizer, getitimer) { EXPECT_POISONED(it1.it_value.tv_sec); EXPECT_POISONED(it1.it_value.tv_usec); res = getitimer(ITIMER_VIRTUAL, &it1); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it1.it_interval.tv_sec); EXPECT_NOT_POISONED(it1.it_interval.tv_usec); EXPECT_NOT_POISONED(it1.it_value.tv_sec); @@ -1833,7 +1956,7 @@ TEST(MemorySanitizer, getitimer) { it1.it_interval.tv_usec = it1.it_value.tv_usec = 0; res = setitimer(ITIMER_VIRTUAL, &it1, &it2); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it2.it_interval.tv_sec); EXPECT_NOT_POISONED(it2.it_interval.tv_usec); EXPECT_NOT_POISONED(it2.it_value.tv_sec); @@ -1842,7 +1965,7 @@ TEST(MemorySanitizer, getitimer) { // Check that old_value can be 0, and disable the timer. memset(&it1, 0, sizeof(it1)); res = setitimer(ITIMER_VIRTUAL, &it1, 0); - assert(!res); + ASSERT_EQ(0, res); } TEST(MemorySanitizer, setitimer_null) { @@ -1856,14 +1979,14 @@ TEST(MemorySanitizer, time) { time_t t; EXPECT_POISONED(t); time_t t2 = time(&t); - assert(t2 != (time_t)-1); + ASSERT_NE(t2, (time_t)-1); EXPECT_NOT_POISONED(t); } TEST(MemorySanitizer, strptime) { struct tm time; char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); - assert(p != 0); + ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); @@ -1872,34 +1995,34 @@ TEST(MemorySanitizer, strptime) { TEST(MemorySanitizer, localtime) { time_t t = 123; struct tm *time = localtime(&t); - assert(time != 0); + ASSERT_TRUE(time != NULL); EXPECT_NOT_POISONED(time->tm_sec); EXPECT_NOT_POISONED(time->tm_hour); EXPECT_NOT_POISONED(time->tm_year); EXPECT_NOT_POISONED(time->tm_isdst); - EXPECT_NE(0, strlen(time->tm_zone)); + EXPECT_NE(0U, strlen(time->tm_zone)); } TEST(MemorySanitizer, localtime_r) { time_t t = 123; struct tm time; struct tm *res = localtime_r(&t, &time); - assert(res != 0); + ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); EXPECT_NOT_POISONED(time.tm_isdst); - EXPECT_NE(0, strlen(time.tm_zone)); + EXPECT_NE(0U, strlen(time.tm_zone)); } TEST(MemorySanitizer, getmntent) { FILE *fp = setmntent("/etc/fstab", "r"); struct mntent *mnt = getmntent(fp); - ASSERT_NE((void *)0, mnt); - ASSERT_NE(0, strlen(mnt->mnt_fsname)); - ASSERT_NE(0, strlen(mnt->mnt_dir)); - ASSERT_NE(0, strlen(mnt->mnt_type)); - ASSERT_NE(0, strlen(mnt->mnt_opts)); + ASSERT_TRUE(mnt != NULL); + ASSERT_NE(0U, strlen(mnt->mnt_fsname)); + ASSERT_NE(0U, strlen(mnt->mnt_dir)); + ASSERT_NE(0U, strlen(mnt->mnt_type)); + ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); @@ -1910,11 +2033,11 @@ TEST(MemorySanitizer, getmntent_r) { struct mntent mntbuf; char buf[1000]; struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); - ASSERT_NE((void *)0, mnt); - ASSERT_NE(0, strlen(mnt->mnt_fsname)); - ASSERT_NE(0, strlen(mnt->mnt_dir)); - ASSERT_NE(0, strlen(mnt->mnt_type)); - ASSERT_NE(0, strlen(mnt->mnt_opts)); + ASSERT_TRUE(mnt != NULL); + ASSERT_NE(0U, strlen(mnt->mnt_fsname)); + ASSERT_NE(0U, strlen(mnt->mnt_dir)); + ASSERT_NE(0U, strlen(mnt->mnt_type)); + ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); @@ -1931,12 +2054,12 @@ TEST(MemorySanitizer, ether) { EXPECT_NOT_POISONED(addr); char *s = ether_ntoa(&addr); - ASSERT_NE(0, strlen(s)); + ASSERT_NE(0U, strlen(s)); char buf[100]; s = ether_ntoa_r(&addr, buf); ASSERT_EQ(s, buf); - ASSERT_NE(0, strlen(buf)); + ASSERT_NE(0U, strlen(buf)); } TEST(MemorySanitizer, mmap) { @@ -1969,6 +2092,56 @@ TEST(MemorySanitizer, fcvt) { char *str = fcvt(12345.6789, 10, &a, &b); EXPECT_NOT_POISONED(a); EXPECT_NOT_POISONED(b); + ASSERT_NE(nullptr, str); + EXPECT_NOT_POISONED(str[0]); + ASSERT_NE(0U, strlen(str)); +} + +TEST(MemorySanitizer, fcvt_long) { + int a, b; + break_optimization(&a); + break_optimization(&b); + EXPECT_POISONED(a); + EXPECT_POISONED(b); + char *str = fcvt(111111112345.6789, 10, &a, &b); + EXPECT_NOT_POISONED(a); + EXPECT_NOT_POISONED(b); + ASSERT_NE(nullptr, str); + EXPECT_NOT_POISONED(str[0]); + ASSERT_NE(0U, strlen(str)); +} + + +TEST(MemorySanitizer, memchr) { + char x[10]; + break_optimization(x); + EXPECT_POISONED(x[0]); + x[2] = '2'; + void *res; + EXPECT_UMR(res = memchr(x, '2', 10)); + EXPECT_NOT_POISONED(res); + x[0] = '0'; + x[1] = '1'; + res = memchr(x, '2', 10); + EXPECT_EQ(&x[2], res); + EXPECT_UMR(res = memchr(x, '3', 10)); + EXPECT_NOT_POISONED(res); +} + +TEST(MemorySanitizer, memrchr) { + char x[10]; + break_optimization(x); + EXPECT_POISONED(x[0]); + x[9] = '9'; + void *res; + EXPECT_UMR(res = memrchr(x, '9', 10)); + EXPECT_NOT_POISONED(res); + x[0] = '0'; + x[1] = '1'; + res = memrchr(x, '0', 2); + EXPECT_EQ(&x[0], res); + EXPECT_UMR(res = memrchr(x, '7', 10)); + EXPECT_NOT_POISONED(res); } TEST(MemorySanitizer, frexp) { @@ -1994,8 +2167,8 @@ namespace { static int cnt; void SigactionHandler(int signo, siginfo_t* si, void* uc) { - assert(signo == SIGPROF); - assert(si); + ASSERT_EQ(signo, SIGPROF); + ASSERT_TRUE(si != NULL); EXPECT_NOT_POISONED(si->si_errno); EXPECT_NOT_POISONED(si->si_pid); #if __linux__ @@ -2308,6 +2481,41 @@ struct StructByVal { int a, b, c, d, e, f; }; +static void vaargsfn_structbyval(int guard, ...) { + va_list vl; + va_start(vl, guard); + { + StructByVal s = va_arg(vl, StructByVal); + EXPECT_NOT_POISONED(s.a); + EXPECT_POISONED(s.b); + EXPECT_NOT_POISONED(s.c); + EXPECT_POISONED(s.d); + EXPECT_NOT_POISONED(s.e); + EXPECT_POISONED(s.f); + } + { + StructByVal s = va_arg(vl, StructByVal); + EXPECT_NOT_POISONED(s.a); + EXPECT_POISONED(s.b); + EXPECT_NOT_POISONED(s.c); + EXPECT_POISONED(s.d); + EXPECT_NOT_POISONED(s.e); + EXPECT_POISONED(s.f); + } + va_end(vl); +} + +TEST(MemorySanitizer, VAArgStructByVal) { + StructByVal s; + s.a = 1; + s.b = *GetPoisoned<int>(); + s.c = 2; + s.d = *GetPoisoned<int>(); + s.e = 3; + s.f = *GetPoisoned<int>(); + vaargsfn_structbyval(0, s, s); +} + NOINLINE void StructByValTestFunc(struct StructByVal s) { EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); @@ -2481,7 +2689,7 @@ TEST(MemorySanitizer, getrlimit) { struct rlimit limit; __msan_poison(&limit, sizeof(limit)); int result = getrlimit(RLIMIT_DATA, &limit); - assert(result == 0); + ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(limit.rlim_cur); EXPECT_NOT_POISONED(limit.rlim_max); } @@ -2490,7 +2698,7 @@ TEST(MemorySanitizer, getrusage) { struct rusage usage; __msan_poison(&usage, sizeof(usage)); int result = getrusage(RUSAGE_SELF, &usage); - assert(result == 0); + ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(usage.ru_utime.tv_sec); EXPECT_NOT_POISONED(usage.ru_utime.tv_usec); EXPECT_NOT_POISONED(usage.ru_stime.tv_sec); @@ -2516,7 +2724,7 @@ TEST(MemorySanitizer, dladdr) { Dl_info info; __msan_poison(&info, sizeof(info)); int result = dladdr((const void*)dladdr_testfn, &info); - assert(result != 0); + ASSERT_NE(result, 0); EXPECT_NOT_POISONED((unsigned long)info.dli_fname); if (info.dli_fname) EXPECT_NOT_POISONED(strlen(info.dli_fname)); @@ -2548,13 +2756,14 @@ static int PathToLoadable(char *buf, size_t sz) { assert(last_slash); int res = snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); - return res < sz ? 0 : res; + assert(res >= 0); + return (size_t)res < sz ? 0 : res; } TEST(MemorySanitizer, dl_iterate_phdr) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); - assert(!res); + ASSERT_EQ(0, res); // Having at least one dlopen'ed library in the process makes this more // entertaining. @@ -2563,7 +2772,7 @@ TEST(MemorySanitizer, dl_iterate_phdr) { int count = 0; int result = dl_iterate_phdr(dl_phdr_callback, &count); - assert(count > 0); + ASSERT_GT(count, 0); dlclose(lib); } @@ -2572,7 +2781,7 @@ TEST(MemorySanitizer, dl_iterate_phdr) { TEST(MemorySanitizer, dlopen) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); - assert(!res); + ASSERT_EQ(0, res); // We need to clear shadow for globals when doing dlopen. In order to test // this, we have to poison the shadow for the DSO before we load it. In @@ -2583,10 +2792,10 @@ TEST(MemorySanitizer, dlopen) { void *lib = dlopen(path, RTLD_LAZY); if (lib == NULL) { printf("dlerror: %s\n", dlerror()); - assert(lib != NULL); + ASSERT_TRUE(lib != NULL); } void **(*get_dso_global)() = (void **(*)())dlsym(lib, "get_dso_global"); - assert(get_dso_global); + ASSERT_TRUE(get_dso_global != NULL); void **dso_global = get_dso_global(); EXPECT_NOT_POISONED(*dso_global); __msan_poison(dso_global, sizeof(*dso_global)); @@ -2599,7 +2808,7 @@ TEST(MemorySanitizer, dlopen) { TEST(MemorySanitizer, dlopenFailed) { const char *path = "/libmsan_loadable_does_not_exist.x86_64.so"; void *lib = dlopen(path, RTLD_LAZY); - ASSERT_EQ(0, lib); + ASSERT_TRUE(lib == NULL); } #endif // MSAN_TEST_DISABLE_DLOPEN @@ -2617,7 +2826,7 @@ TEST(MemorySanitizer, scanf) { char* s = new char[7]; int res = sscanf(input, "%d %5s", d, s); printf("res %d\n", res); - assert(res == 2); + ASSERT_EQ(res, 2); EXPECT_NOT_POISONED(*d); EXPECT_NOT_POISONED(s[0]); EXPECT_NOT_POISONED(s[1]); @@ -2626,7 +2835,7 @@ TEST(MemorySanitizer, scanf) { EXPECT_NOT_POISONED(s[4]); EXPECT_NOT_POISONED(s[5]); EXPECT_POISONED(s[6]); - delete s; + delete[] s; delete d; } @@ -2638,10 +2847,10 @@ TEST(MemorySanitizer, SimpleThread) { pthread_t t; void *p; int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(t); res = pthread_join(t, &p); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); delete (int*)p; } @@ -2667,22 +2876,22 @@ TEST(MemorySanitizer, SmallStackThread) { ASSERT_EQ(0, res); } -TEST(MemorySanitizer, PreAllocatedStackThread) { +TEST(MemorySanitizer, SmallPreAllocatedStackThread) { pthread_attr_t attr; pthread_t t; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); void *stack; - const size_t kStackSize = 64 * 1024; + const size_t kStackSize = 16 * 1024; res = posix_memalign(&stack, 4096, kStackSize); ASSERT_EQ(0, res); res = pthread_attr_setstack(&attr, stack, kStackSize); ASSERT_EQ(0, res); - // A small self-allocated stack can not be extended by the tool. - // In this case pthread_create is expected to fail. res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); - EXPECT_NE(0, res); + EXPECT_EQ(0, res); + res = pthread_join(t, NULL); + ASSERT_EQ(0, res); res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } @@ -2764,10 +2973,10 @@ TEST(MemorySanitizer, pthread_getschedparam) { TEST(MemorySanitizer, pthread_key_create) { pthread_key_t key; int res = pthread_key_create(&key, NULL); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(key); res = pthread_key_delete(key); - assert(!res); + ASSERT_EQ(0, res); } namespace { @@ -2801,14 +3010,14 @@ TEST(MemorySanitizer, pthread_cond_wait) { pthread_t thr; pthread_create(&thr, 0, SignalCond, &args); int res = pthread_cond_wait(&cond, &mu); - assert(!res); + ASSERT_EQ(0, res); pthread_join(thr, 0); // broadcast args.broadcast = true; pthread_create(&thr, 0, SignalCond, &args); res = pthread_cond_wait(&cond, &mu); - assert(!res); + ASSERT_EQ(0, res); pthread_join(thr, 0); pthread_mutex_unlock(&mu); @@ -2854,12 +3063,12 @@ TEST(MemorySanitizer, valloc) { TEST(MemorySanitizer, pvalloc) { void *p = pvalloc(kPageSize + 100); EXPECT_EQ(0U, (uintptr_t)p % kPageSize); - EXPECT_EQ(2 * kPageSize, __msan_get_allocated_size(p)); + EXPECT_EQ(2 * kPageSize, __sanitizer_get_allocated_size(p)); free(p); p = pvalloc(0); // pvalloc(0) should allocate at least one page. EXPECT_EQ(0U, (uintptr_t)p % kPageSize); - EXPECT_EQ(kPageSize, __msan_get_allocated_size(p)); + EXPECT_EQ(kPageSize, __sanitizer_get_allocated_size(p)); free(p); } @@ -2890,7 +3099,7 @@ TEST(MemorySanitizer, inet_aton) { TEST(MemorySanitizer, uname) { struct utsname u; int res = uname(&u); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(u.sysname)); EXPECT_NOT_POISONED(strlen(u.nodename)); EXPECT_NOT_POISONED(strlen(u.release)); @@ -2901,25 +3110,39 @@ TEST(MemorySanitizer, uname) { TEST(MemorySanitizer, gethostname) { char buf[100]; int res = gethostname(buf, 100); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(buf)); } TEST(MemorySanitizer, sysinfo) { struct sysinfo info; int res = sysinfo(&info); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(info); } TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root - assert(p); + ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); - assert(p->pw_name); + ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); - assert(p->pw_uid == 0); + ASSERT_EQ(0U, p->pw_uid); +} + +TEST(MemorySanitizer, getpwuid_r) { + struct passwd pwd; + struct passwd *pwdres; + char buf[10000]; + int res = getpwuid_r(0, &pwd, buf, sizeof(buf), &pwdres); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(pwd.pw_name); + ASSERT_TRUE(pwd.pw_name != NULL); + EXPECT_NOT_POISONED(pwd.pw_name[0]); + EXPECT_NOT_POISONED(pwd.pw_uid); + ASSERT_EQ(0U, pwd.pw_uid); + EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r) { @@ -2927,12 +3150,13 @@ TEST(MemorySanitizer, getpwnam_r) { struct passwd *pwdres; char buf[10000]; int res = getpwnam_r("root", &pwd, buf, sizeof(buf), &pwdres); - assert(!res); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); - assert(pwd.pw_name); + ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); - assert(pwd.pw_uid == 0); + ASSERT_EQ(0U, pwd.pw_uid); + EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r_positive) { @@ -2951,11 +3175,102 @@ TEST(MemorySanitizer, getgrnam_r) { struct group *grpres; char buf[10000]; int res = getgrnam_r("root", &grp, buf, sizeof(buf), &grpres); - assert(!res); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(grp.gr_name); + ASSERT_TRUE(grp.gr_name != NULL); + EXPECT_NOT_POISONED(grp.gr_name[0]); + EXPECT_NOT_POISONED(grp.gr_gid); + EXPECT_NOT_POISONED(grpres); +} + +TEST(MemorySanitizer, getpwent) { + setpwent(); + struct passwd *p = getpwent(); + ASSERT_TRUE(p != NULL); + EXPECT_NOT_POISONED(p->pw_name); + ASSERT_TRUE(p->pw_name != NULL); + EXPECT_NOT_POISONED(p->pw_name[0]); + EXPECT_NOT_POISONED(p->pw_uid); +} + +TEST(MemorySanitizer, getpwent_r) { + struct passwd pwd; + struct passwd *pwdres; + char buf[10000]; + setpwent(); + int res = getpwent_r(&pwd, buf, sizeof(buf), &pwdres); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(pwd.pw_name); + ASSERT_TRUE(pwd.pw_name != NULL); + EXPECT_NOT_POISONED(pwd.pw_name[0]); + EXPECT_NOT_POISONED(pwd.pw_uid); + EXPECT_NOT_POISONED(pwdres); +} + +TEST(MemorySanitizer, fgetpwent) { + FILE *fp = fopen("/etc/passwd", "r"); + struct passwd *p = fgetpwent(fp); + ASSERT_TRUE(p != NULL); + EXPECT_NOT_POISONED(p->pw_name); + ASSERT_TRUE(p->pw_name != NULL); + EXPECT_NOT_POISONED(p->pw_name[0]); + EXPECT_NOT_POISONED(p->pw_uid); + fclose(fp); +} + +TEST(MemorySanitizer, getgrent) { + setgrent(); + struct group *p = getgrent(); + ASSERT_TRUE(p != NULL); + EXPECT_NOT_POISONED(p->gr_name); + ASSERT_TRUE(p->gr_name != NULL); + EXPECT_NOT_POISONED(p->gr_name[0]); + EXPECT_NOT_POISONED(p->gr_gid); +} + +TEST(MemorySanitizer, fgetgrent) { + FILE *fp = fopen("/etc/group", "r"); + struct group *grp = fgetgrent(fp); + ASSERT_TRUE(grp != NULL); + EXPECT_NOT_POISONED(grp->gr_name); + ASSERT_TRUE(grp->gr_name != NULL); + EXPECT_NOT_POISONED(grp->gr_name[0]); + EXPECT_NOT_POISONED(grp->gr_gid); + for (char **p = grp->gr_mem; *p; ++p) { + EXPECT_NOT_POISONED((*p)[0]); + EXPECT_TRUE(strlen(*p) > 0); + } + fclose(fp); +} + +TEST(MemorySanitizer, getgrent_r) { + struct group grp; + struct group *grpres; + char buf[10000]; + setgrent(); + int res = getgrent_r(&grp, buf, sizeof(buf), &grpres); + ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); - assert(grp.gr_name); + ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); + EXPECT_NOT_POISONED(grpres); +} + +TEST(MemorySanitizer, fgetgrent_r) { + FILE *fp = fopen("/etc/group", "r"); + struct group grp; + struct group *grpres; + char buf[10000]; + setgrent(); + int res = fgetgrent_r(fp, &grp, buf, sizeof(buf), &grpres); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(grp.gr_name); + ASSERT_TRUE(grp.gr_name != NULL); + EXPECT_NOT_POISONED(grp.gr_name[0]); + EXPECT_NOT_POISONED(grp.gr_gid); + EXPECT_NOT_POISONED(grpres); + fclose(fp); } TEST(MemorySanitizer, getgroups) { @@ -2971,7 +3286,7 @@ TEST(MemorySanitizer, wordexp) { wordexp_t w; int res = wordexp("a b c", &w, 0); ASSERT_EQ(0, res); - ASSERT_EQ(3, w.we_wordc); + ASSERT_EQ(3U, w.we_wordc); ASSERT_STREQ("a", w.we_wordv[0]); ASSERT_STREQ("b", w.we_wordv[1]); ASSERT_STREQ("c", w.we_wordv[2]); @@ -3085,141 +3400,376 @@ TEST(MemorySanitizer, VolatileBitfield) { TEST(MemorySanitizer, UnalignedLoad) { char x[32]; + U4 origin = __LINE__; + for (unsigned i = 0; i < sizeof(x) / 4; ++i) + __msan_set_origin(x + 4 * i, 4, origin + i); + memset(x + 8, 0, 16); - EXPECT_POISONED(__sanitizer_unaligned_load16(x+6)); - EXPECT_POISONED(__sanitizer_unaligned_load16(x+7)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x+22)); - EXPECT_POISONED(__sanitizer_unaligned_load16(x+23)); - EXPECT_POISONED(__sanitizer_unaligned_load16(x+24)); - - EXPECT_POISONED(__sanitizer_unaligned_load32(x+4)); - EXPECT_POISONED(__sanitizer_unaligned_load32(x+7)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x+20)); - EXPECT_POISONED(__sanitizer_unaligned_load32(x+21)); - EXPECT_POISONED(__sanitizer_unaligned_load32(x+24)); - - EXPECT_POISONED(__sanitizer_unaligned_load64(x)); - EXPECT_POISONED(__sanitizer_unaligned_load64(x+1)); - EXPECT_POISONED(__sanitizer_unaligned_load64(x+7)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+8)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+9)); - EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x+16)); - EXPECT_POISONED(__sanitizer_unaligned_load64(x+17)); - EXPECT_POISONED(__sanitizer_unaligned_load64(x+21)); - EXPECT_POISONED(__sanitizer_unaligned_load64(x+24)); + EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 6), origin + 1); + EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 7), origin + 1); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 22)); + EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 23), origin + 6); + EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 24), origin + 6); + + EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 4), origin + 1); + EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 7), origin + 1); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 20)); + EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 21), origin + 6); + EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 24), origin + 6); + + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x), origin); + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 1), origin); + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 7), origin + 1); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 8)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 9)); + EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 16)); + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 17), origin + 6); + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 21), origin + 6); + EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 24), origin + 6); } TEST(MemorySanitizer, UnalignedStore16) { char x[5]; - U2 y = 0; - __msan_poison(&y, 1); - __sanitizer_unaligned_store16(x + 1, y); - EXPECT_POISONED(x[0]); - EXPECT_POISONED(x[1]); + U2 y2 = 0; + U4 origin = __LINE__; + __msan_poison(&y2, 1); + __msan_set_origin(&y2, 1, origin); + + __sanitizer_unaligned_store16(x + 1, y2); + EXPECT_POISONED_O(x[0], origin); + EXPECT_POISONED_O(x[1], origin); EXPECT_NOT_POISONED(x[2]); - EXPECT_POISONED(x[3]); - EXPECT_POISONED(x[4]); + EXPECT_POISONED_O(x[3], origin); + EXPECT_POISONED_O(x[4], origin); } TEST(MemorySanitizer, UnalignedStore32) { char x[8]; U4 y4 = 0; + U4 origin = __LINE__; __msan_poison(&y4, 2); - __sanitizer_unaligned_store32(x+3, y4); - EXPECT_POISONED(x[0]); - EXPECT_POISONED(x[1]); - EXPECT_POISONED(x[2]); - EXPECT_POISONED(x[3]); - EXPECT_POISONED(x[4]); + __msan_set_origin(&y4, 2, origin); + + __sanitizer_unaligned_store32(x + 3, y4); + EXPECT_POISONED_O(x[0], origin); + EXPECT_POISONED_O(x[1], origin); + EXPECT_POISONED_O(x[2], origin); + EXPECT_POISONED_O(x[3], origin); + EXPECT_POISONED_O(x[4], origin); EXPECT_NOT_POISONED(x[5]); EXPECT_NOT_POISONED(x[6]); - EXPECT_POISONED(x[7]); + EXPECT_POISONED_O(x[7], origin); } TEST(MemorySanitizer, UnalignedStore64) { char x[16]; - U8 y = 0; - __msan_poison(&y, 3); - __msan_poison(((char *)&y) + sizeof(y) - 2, 1); - __sanitizer_unaligned_store64(x+3, y); - EXPECT_POISONED(x[0]); - EXPECT_POISONED(x[1]); - EXPECT_POISONED(x[2]); - EXPECT_POISONED(x[3]); - EXPECT_POISONED(x[4]); - EXPECT_POISONED(x[5]); + U8 y8 = 0; + U4 origin = __LINE__; + __msan_poison(&y8, 3); + __msan_poison(((char *)&y8) + sizeof(y8) - 2, 1); + __msan_set_origin(&y8, 8, origin); + + __sanitizer_unaligned_store64(x + 3, y8); + EXPECT_POISONED_O(x[0], origin); + EXPECT_POISONED_O(x[1], origin); + EXPECT_POISONED_O(x[2], origin); + EXPECT_POISONED_O(x[3], origin); + EXPECT_POISONED_O(x[4], origin); + EXPECT_POISONED_O(x[5], origin); EXPECT_NOT_POISONED(x[6]); EXPECT_NOT_POISONED(x[7]); EXPECT_NOT_POISONED(x[8]); - EXPECT_POISONED(x[9]); + EXPECT_POISONED_O(x[9], origin); EXPECT_NOT_POISONED(x[10]); - EXPECT_POISONED(x[11]); + EXPECT_POISONED_O(x[11], origin); } -TEST(MemorySanitizerDr, StoreInDSOTest) { - if (!__msan_has_dynamic_component()) return; - char* s = new char[10]; - dso_memfill(s, 9); - EXPECT_NOT_POISONED(s[5]); - EXPECT_POISONED(s[9]); +TEST(MemorySanitizer, UnalignedStore16_precise) { + char x[8]; + U2 y = 0; + U4 originx1 = __LINE__; + U4 originx2 = __LINE__; + U4 originy = __LINE__; + __msan_poison(x, sizeof(x)); + __msan_set_origin(x, 4, originx1); + __msan_set_origin(x + 4, 4, originx2); + __msan_poison(((char *)&y) + 1, 1); + __msan_set_origin(&y, sizeof(y), originy); + + __sanitizer_unaligned_store16(x + 3, y); + EXPECT_POISONED_O(x[0], originx1); + EXPECT_POISONED_O(x[1], originx1); + EXPECT_POISONED_O(x[2], originx1); + EXPECT_NOT_POISONED(x[3]); + EXPECT_POISONED_O(x[4], originy); + EXPECT_POISONED_O(x[5], originy); + EXPECT_POISONED_O(x[6], originy); + EXPECT_POISONED_O(x[7], originy); } -int return_poisoned_int() { - return ReturnPoisoned<U8>(); -} +TEST(MemorySanitizer, UnalignedStore16_precise2) { + char x[8]; + U2 y = 0; + U4 originx1 = __LINE__; + U4 originx2 = __LINE__; + U4 originy = __LINE__; + __msan_poison(x, sizeof(x)); + __msan_set_origin(x, 4, originx1); + __msan_set_origin(x + 4, 4, originx2); + __msan_poison(((char *)&y), 1); + __msan_set_origin(&y, sizeof(y), originy); + + __sanitizer_unaligned_store16(x + 3, y); + EXPECT_POISONED_O(x[0], originy); + EXPECT_POISONED_O(x[1], originy); + EXPECT_POISONED_O(x[2], originy); + EXPECT_POISONED_O(x[3], originy); + EXPECT_NOT_POISONED(x[4]); + EXPECT_POISONED_O(x[5], originx2); + EXPECT_POISONED_O(x[6], originx2); + EXPECT_POISONED_O(x[7], originx2); +} + +TEST(MemorySanitizer, UnalignedStore64_precise) { + char x[12]; + U8 y = 0; + U4 originx1 = __LINE__; + U4 originx2 = __LINE__; + U4 originx3 = __LINE__; + U4 originy = __LINE__; + __msan_poison(x, sizeof(x)); + __msan_set_origin(x, 4, originx1); + __msan_set_origin(x + 4, 4, originx2); + __msan_set_origin(x + 8, 4, originx3); + __msan_poison(((char *)&y) + 1, 1); + __msan_poison(((char *)&y) + 7, 1); + __msan_set_origin(&y, sizeof(y), originy); + + __sanitizer_unaligned_store64(x + 2, y); + EXPECT_POISONED_O(x[0], originy); + EXPECT_POISONED_O(x[1], originy); + EXPECT_NOT_POISONED(x[2]); + EXPECT_POISONED_O(x[3], originy); + + EXPECT_NOT_POISONED(x[4]); + EXPECT_NOT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_NOT_POISONED(x[7]); -TEST(MemorySanitizerDr, ReturnFromDSOTest) { - if (!__msan_has_dynamic_component()) return; - EXPECT_NOT_POISONED(dso_callfn(return_poisoned_int)); + EXPECT_NOT_POISONED(x[8]); + EXPECT_POISONED_O(x[9], originy); + EXPECT_POISONED_O(x[10], originy); + EXPECT_POISONED_O(x[11], originy); } -NOINLINE int TrashParamTLS(long long x, long long y, long long z) { //NOLINT - EXPECT_POISONED(x); - EXPECT_POISONED(y); - EXPECT_POISONED(z); - return 0; +TEST(MemorySanitizer, UnalignedStore64_precise2) { + char x[12]; + U8 y = 0; + U4 originx1 = __LINE__; + U4 originx2 = __LINE__; + U4 originx3 = __LINE__; + U4 originy = __LINE__; + __msan_poison(x, sizeof(x)); + __msan_set_origin(x, 4, originx1); + __msan_set_origin(x + 4, 4, originx2); + __msan_set_origin(x + 8, 4, originx3); + __msan_poison(((char *)&y) + 3, 3); + __msan_set_origin(&y, sizeof(y), originy); + + __sanitizer_unaligned_store64(x + 2, y); + EXPECT_POISONED_O(x[0], originx1); + EXPECT_POISONED_O(x[1], originx1); + EXPECT_NOT_POISONED(x[2]); + EXPECT_NOT_POISONED(x[3]); + + EXPECT_NOT_POISONED(x[4]); + EXPECT_POISONED_O(x[5], originy); + EXPECT_POISONED_O(x[6], originy); + EXPECT_POISONED_O(x[7], originy); + + EXPECT_NOT_POISONED(x[8]); + EXPECT_NOT_POISONED(x[9]); + EXPECT_POISONED_O(x[10], originx3); + EXPECT_POISONED_O(x[11], originx3); } -static int CheckParamTLS(long long x, long long y, long long z) { //NOLINT - EXPECT_NOT_POISONED(x); - EXPECT_NOT_POISONED(y); - EXPECT_NOT_POISONED(z); - return 0; +#if defined(__clang__) +namespace { +typedef U1 V16x8 __attribute__((__vector_size__(16))); +typedef U2 V8x16 __attribute__((__vector_size__(16))); +typedef U4 V4x32 __attribute__((__vector_size__(16))); +typedef U8 V2x64 __attribute__((__vector_size__(16))); +typedef U4 V8x32 __attribute__((__vector_size__(32))); +typedef U8 V4x64 __attribute__((__vector_size__(32))); +typedef U4 V2x32 __attribute__((__vector_size__(8))); +typedef U2 V4x16 __attribute__((__vector_size__(8))); +typedef U1 V8x8 __attribute__((__vector_size__(8))); + + +V8x16 shift_sse2_left_scalar(V8x16 x, U4 y) { + return _mm_slli_epi16(x, y); +} + +V8x16 shift_sse2_left(V8x16 x, V8x16 y) { + return _mm_sll_epi16(x, y); +} + +TEST(VectorShiftTest, sse2_left_scalar) { + V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; + V8x16 u = shift_sse2_left_scalar(v, 2); + EXPECT_POISONED(u[0]); + EXPECT_POISONED(u[1]); + EXPECT_NOT_POISONED(u[0] | (3U << 2)); + EXPECT_NOT_POISONED(u[1] | (7U << 2)); + u[0] = u[1] = 0; + EXPECT_NOT_POISONED(u); +} + +TEST(VectorShiftTest, sse2_left_scalar_by_uninit) { + V8x16 v = {0, 1, 2, 3, 4, 5, 6, 7}; + V8x16 u = shift_sse2_left_scalar(v, Poisoned<U4>()); + EXPECT_POISONED(u[0]); + EXPECT_POISONED(u[1]); + EXPECT_POISONED(u[2]); + EXPECT_POISONED(u[3]); + EXPECT_POISONED(u[4]); + EXPECT_POISONED(u[5]); + EXPECT_POISONED(u[6]); + EXPECT_POISONED(u[7]); +} + +TEST(VectorShiftTest, sse2_left) { + V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; + // Top 64 bits of shift count don't affect the result. + V2x64 s = {2, Poisoned<U8>()}; + V8x16 u = shift_sse2_left(v, s); + EXPECT_POISONED(u[0]); + EXPECT_POISONED(u[1]); + EXPECT_NOT_POISONED(u[0] | (3U << 2)); + EXPECT_NOT_POISONED(u[1] | (7U << 2)); + u[0] = u[1] = 0; + EXPECT_NOT_POISONED(u); +} + +TEST(VectorShiftTest, sse2_left_by_uninit) { + V8x16 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3, 4, 5, 6, 7}; + V2x64 s = {Poisoned<U8>(), Poisoned<U8>()}; + V8x16 u = shift_sse2_left(v, s); + EXPECT_POISONED(u[0]); + EXPECT_POISONED(u[1]); + EXPECT_POISONED(u[2]); + EXPECT_POISONED(u[3]); + EXPECT_POISONED(u[4]); + EXPECT_POISONED(u[5]); + EXPECT_POISONED(u[6]); + EXPECT_POISONED(u[7]); +} + +#ifdef __AVX2__ +V4x32 shift_avx2_left(V4x32 x, V4x32 y) { + return _mm_sllv_epi32(x, y); +} +// This is variable vector shift that's only available starting with AVX2. +// V4x32 shift_avx2_left(V4x32 x, V4x32 y) { +TEST(VectorShiftTest, avx2_left) { + V4x32 v = {Poisoned<U2>(0, 3), Poisoned<U2>(0, 7), 2, 3}; + V4x32 s = {2, Poisoned<U4>(), 3, Poisoned<U4>()}; + V4x32 u = shift_avx2_left(v, s); + EXPECT_POISONED(u[0]); + EXPECT_NOT_POISONED(u[0] | (~7U)); + EXPECT_POISONED(u[1]); + EXPECT_POISONED(u[1] | (~31U)); + EXPECT_NOT_POISONED(u[2]); + EXPECT_POISONED(u[3]); + EXPECT_POISONED(u[3] | (~31U)); +} +#endif // __AVX2__ +} // namespace + +TEST(VectorPackTest, sse2_packssdw_128) { + const unsigned S2_max = (1 << 15) - 1; + V4x32 a = {Poisoned<U4>(0, 0xFF0000), Poisoned<U4>(0, 0xFFFF0000), + S2_max + 100, 4}; + V4x32 b = {Poisoned<U4>(0, 0xFF), S2_max + 10000, Poisoned<U4>(0, 0xFF00), + S2_max}; + + V8x16 c = _mm_packs_epi32(a, b); + + EXPECT_POISONED(c[0]); + EXPECT_POISONED(c[1]); + EXPECT_NOT_POISONED(c[2]); + EXPECT_NOT_POISONED(c[3]); + EXPECT_POISONED(c[4]); + EXPECT_NOT_POISONED(c[5]); + EXPECT_POISONED(c[6]); + EXPECT_NOT_POISONED(c[7]); + + EXPECT_EQ(c[2], S2_max); + EXPECT_EQ(c[3], 4); + EXPECT_EQ(c[5], S2_max); + EXPECT_EQ(c[7], S2_max); } -TEST(MemorySanitizerDr, CallFromDSOTest) { - if (!__msan_has_dynamic_component()) return; - S8* x = GetPoisoned<S8>(); - S8* y = GetPoisoned<S8>(); - S8* z = GetPoisoned<S8>(); - EXPECT_NOT_POISONED(TrashParamTLS(*x, *y, *z)); - EXPECT_NOT_POISONED(dso_callfn1(CheckParamTLS)); +TEST(VectorPackTest, mmx_packuswb) { + const unsigned U1_max = (1 << 8) - 1; + V4x16 a = {Poisoned<U2>(0, 0xFF00), Poisoned<U2>(0, 0xF000U), U1_max + 100, + 4}; + V4x16 b = {Poisoned<U2>(0, 0xFF), U1_max - 1, Poisoned<U2>(0, 0xF), U1_max}; + V8x8 c = _mm_packs_pu16(a, b); + + EXPECT_POISONED(c[0]); + EXPECT_POISONED(c[1]); + EXPECT_NOT_POISONED(c[2]); + EXPECT_NOT_POISONED(c[3]); + EXPECT_POISONED(c[4]); + EXPECT_NOT_POISONED(c[5]); + EXPECT_POISONED(c[6]); + EXPECT_NOT_POISONED(c[7]); + + EXPECT_EQ(c[2], U1_max); + EXPECT_EQ(c[3], 4); + EXPECT_EQ(c[5], U1_max - 1); + EXPECT_EQ(c[7], U1_max); } -static void StackStoreInDSOFn(int* x, int* y) { - EXPECT_NOT_POISONED(*x); - EXPECT_NOT_POISONED(*y); +TEST(VectorSadTest, sse2_psad_bw) { + V16x8 a = {Poisoned<U1>(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + V16x8 b = {100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115}; + V2x64 c = _mm_sad_epu8(a, b); + + EXPECT_POISONED(c[0]); + EXPECT_NOT_POISONED(c[1]); + + EXPECT_EQ(800U, c[1]); } -TEST(MemorySanitizerDr, StackStoreInDSOTest) { - if (!__msan_has_dynamic_component()) return; - dso_stack_store(StackStoreInDSOFn, 1); +TEST(VectorMaddTest, mmx_pmadd_wd) { + V4x16 a = {Poisoned<U2>(), 1, 2, 3}; + V4x16 b = {100, 101, 102, 103}; + V2x32 c = _mm_madd_pi16(a, b); + + EXPECT_POISONED(c[0]); + EXPECT_NOT_POISONED(c[1]); + + EXPECT_EQ((unsigned)(2 * 102 + 3 * 103), c[1]); } +#endif // defined(__clang__) TEST(MemorySanitizerOrigins, SetGet) { EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins()); if (!TrackingOrigins()) return; int x; __msan_set_origin(&x, sizeof(x), 1234); - EXPECT_EQ(1234, __msan_get_origin(&x)); + EXPECT_EQ(1234U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 5678); - EXPECT_EQ(5678, __msan_get_origin(&x)); + EXPECT_EQ(5678U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 0); - EXPECT_EQ(0, __msan_get_origin(&x)); + EXPECT_EQ(0U, __msan_get_origin(&x)); } namespace { @@ -3229,8 +3779,7 @@ struct S { U2 b; }; -// http://code.google.com/p/memory-sanitizer/issues/detail?id=6 -TEST(MemorySanitizerOrigins, DISABLED_InitializedStoreDoesNotChangeOrigin) { +TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) { if (!TrackingOrigins()) return; S s; @@ -3403,29 +3952,6 @@ TEST(MemorySanitizerOrigins, Select) { EXPECT_POISONED_O(g_0 ? 1 : *GetPoisonedO<S4>(0, __LINE__), __LINE__); } -extern "C" -NOINLINE char AllocaTO() { - int ar[100]; - break_optimization(ar); - return ar[10]; - // fprintf(stderr, "Descr: %s\n", - // __msan_get_origin_descr_if_stack(__msan_get_origin_tls())); -} - -TEST(MemorySanitizerOrigins, Alloca) { - if (!TrackingOrigins()) return; - EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); - EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); - EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); - EXPECT_POISONED_S(AllocaTO(), "ar@AllocaTO"); -} - -// FIXME: replace with a lit-like test. -TEST(MemorySanitizerOrigins, DISABLED_AllocaDeath) { - if (!TrackingOrigins()) return; - EXPECT_DEATH(AllocaTO(), "ORIGIN: stack allocation: ar@AllocaTO"); -} - NOINLINE int RetvalOriginTest(U4 origin) { int *a = new int; break_optimization(a); @@ -3513,6 +4039,26 @@ TEST(MemorySanitizer, Select) { EXPECT_POISONED(z); } +TEST(MemorySanitizer, SelectPartial) { + // Precise instrumentation of select. + // Some bits of the result do not depend on select condition, and must stay + // initialized even if select condition is not. These are the bits that are + // equal and initialized in both left and right select arguments. + U4 x = 0xFFFFABCDU; + U4 x_s = 0xFFFF0000U; + __msan_partial_poison(&x, &x_s, sizeof(x)); + U4 y = 0xAB00U; + U1 cond = true; + __msan_poison(&cond, sizeof(cond)); + U4 z = cond ? x : y; + __msan_print_shadow(&z, sizeof(z)); + EXPECT_POISONED(z & 0xFFU); + EXPECT_NOT_POISONED(z & 0xFF00U); + EXPECT_POISONED(z & 0xFF0000U); + EXPECT_POISONED(z & 0xFF000000U); + EXPECT_EQ(0xAB00U, z & 0xFF00U); +} + TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { RecursiveMalloc(22); } @@ -3520,7 +4066,7 @@ TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { size_t sizes[] = {0, 20, 5000, 1<<20}; for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { - size_t alloc_size = __msan_get_estimated_allocated_size(sizes[i]); + size_t alloc_size = __sanitizer_get_estimated_allocated_size(sizes[i]); EXPECT_EQ(alloc_size, sizes[i]); } } @@ -3529,26 +4075,26 @@ TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { char *array = reinterpret_cast<char*>(malloc(100)); int *int_ptr = new int; - EXPECT_TRUE(__msan_get_ownership(array)); - EXPECT_EQ(100, __msan_get_allocated_size(array)); + EXPECT_TRUE(__sanitizer_get_ownership(array)); + EXPECT_EQ(100U, __sanitizer_get_allocated_size(array)); - EXPECT_TRUE(__msan_get_ownership(int_ptr)); - EXPECT_EQ(sizeof(*int_ptr), __msan_get_allocated_size(int_ptr)); + EXPECT_TRUE(__sanitizer_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(*int_ptr), __sanitizer_get_allocated_size(int_ptr)); void *wild_addr = reinterpret_cast<void*>(0x1); - EXPECT_FALSE(__msan_get_ownership(wild_addr)); - EXPECT_EQ(0, __msan_get_allocated_size(wild_addr)); + EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); + EXPECT_EQ(0U, __sanitizer_get_allocated_size(wild_addr)); - EXPECT_FALSE(__msan_get_ownership(array + 50)); - EXPECT_EQ(0, __msan_get_allocated_size(array + 50)); + EXPECT_FALSE(__sanitizer_get_ownership(array + 50)); + EXPECT_EQ(0U, __sanitizer_get_allocated_size(array + 50)); + + // NULL is a valid argument for GetAllocatedSize but is not owned. + EXPECT_FALSE(__sanitizer_get_ownership(NULL)); + EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); - // NULL is a valid argument for GetAllocatedSize but is not owned. - EXPECT_FALSE(__msan_get_ownership(NULL)); - EXPECT_EQ(0, __msan_get_allocated_size(NULL)); - free(array); - EXPECT_FALSE(__msan_get_ownership(array)); - EXPECT_EQ(0, __msan_get_allocated_size(array)); + EXPECT_FALSE(__sanitizer_get_ownership(array)); + EXPECT_EQ(0U, __sanitizer_get_allocated_size(array)); delete int_ptr; } @@ -3559,3 +4105,39 @@ TEST(MemorySanitizer, MlockTest) { EXPECT_EQ(0, munlockall()); EXPECT_EQ(0, munlock((void*)0x987, 0x654)); } + +// Test that LargeAllocator unpoisons memory before releasing it to the OS. +TEST(MemorySanitizer, LargeAllocatorUnpoisonsOnFree) { + void *p = malloc(1024 * 1024); + free(p); + + typedef void *(*mmap_fn)(void *, size_t, int, int, int, off_t); + mmap_fn real_mmap = (mmap_fn)dlsym(RTLD_NEXT, "mmap"); + + // Allocate the page that was released to the OS in free() with the real mmap, + // bypassing the interceptor. + char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + ASSERT_NE((char *)0, q); + + ASSERT_TRUE(q <= p); + ASSERT_TRUE(q + 4096 > p); + + EXPECT_NOT_POISONED(q[0]); + EXPECT_NOT_POISONED(q[10]); + EXPECT_NOT_POISONED(q[100]); + + munmap(q, 4096); +} + +#if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE +TEST(MemorySanitizer, MallocUsableSizeTest) { + const size_t kArraySize = 100; + char *array = Ident((char*)malloc(kArraySize)); + int *int_ptr = Ident(new int); + EXPECT_EQ(0U, malloc_usable_size(NULL)); + EXPECT_EQ(kArraySize, malloc_usable_size(array)); + EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); + free(array); + delete int_ptr; +} +#endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE diff --git a/lib/msan/tests/msandr_test_so.cc b/lib/msan/tests/msandr_test_so.cc deleted file mode 100644 index eb605d4dba12c..0000000000000 --- a/lib/msan/tests/msandr_test_so.cc +++ /dev/null @@ -1,38 +0,0 @@ -//===-- msandr_test_so.cc ------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -// MemorySanitizer unit tests. -//===----------------------------------------------------------------------===// - -#include "msandr_test_so.h" - -void dso_memfill(char* s, unsigned n) { - for (unsigned i = 0; i < n; ++i) - s[i] = i; -} - -int dso_callfn(int (*fn)(void)) { - volatile int x = fn(); - return x; -} - -int dso_callfn1(int (*fn)(long long, long long, long long)) { //NOLINT - volatile int x = fn(1, 2, 3); - return x; -} - -int dso_stack_store(void (*fn)(int*, int*), int x) { - int y = x + 1; - fn(&x, &y); - return y; -} - -void break_optimization(void *x) {} diff --git a/lib/msan/tests/msandr_test_so.h b/lib/msan/tests/msandr_test_so.h deleted file mode 100644 index cd75ff34f3874..0000000000000 --- a/lib/msan/tests/msandr_test_so.h +++ /dev/null @@ -1,24 +0,0 @@ -//===-- msandr_test_so.h ----------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file is a part of MemorySanitizer. -// -// MemorySanitizer unit tests. -//===----------------------------------------------------------------------===// - -#ifndef MSANDR_MSANDR_TEST_SO_H -#define MSANDR_MSANDR_TEST_SO_H - -void dso_memfill(char* s, unsigned n); -int dso_callfn(int (*fn)(void)); -int dso_callfn1(int (*fn)(long long, long long, long long)); //NOLINT -int dso_stack_store(void (*fn)(int*, int*), int x); -void break_optimization(void *x); - -#endif |