diff options
Diffstat (limited to 'lib/msan')
71 files changed, 3382 insertions, 485 deletions
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt index 0671b59c0025..06f3f65d8e38 100644 --- a/lib/msan/CMakeLists.txt +++ b/lib/msan/CMakeLists.txt @@ -25,13 +25,24 @@ if(CAN_TARGET_${arch}) $<TARGET_OBJECTS:RTInterception.${arch}> $<TARGET_OBJECTS:RTSanitizerCommon.${arch}> $<TARGET_OBJECTS:RTSanitizerCommonLibc.${arch}> - CFLAGS ${MSAN_RTL_CFLAGS} - SYMS msan.syms) + CFLAGS ${MSAN_RTL_CFLAGS}) 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) + endif() endif() add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt) +# 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) add_subdirectory(tests) endif() diff --git a/lib/msan/lit_tests/CMakeLists.txt b/lib/msan/lit_tests/CMakeLists.txt index ed2da6b839f5..38d1e59e709e 100644 --- a/lib/msan/lit_tests/CMakeLists.txt +++ b/lib/msan/lit_tests/CMakeLists.txt @@ -3,24 +3,23 @@ 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 - ) + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/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) +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) + 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" diff --git a/lib/msan/lit_tests/Linux/glob.cc b/lib/msan/lit_tests/Linux/glob.cc index 513679c6d3d7..387ce3cf5f1a 100644 --- a/lib/msan/lit_tests/Linux/glob.cc +++ b/lib/msan/lit_tests/Linux/glob.cc @@ -1,4 +1,5 @@ // 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> diff --git a/lib/msan/lit_tests/Linux/glob_altdirfunc.cc b/lib/msan/lit_tests/Linux/glob_altdirfunc.cc new file mode 100644 index 000000000000..b8200c3ee899 --- /dev/null +++ b/lib/msan/lit_tests/Linux/glob_altdirfunc.cc @@ -0,0 +1,78 @@ +// 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 new file mode 100644 index 000000000000..0262034aec5b --- /dev/null +++ b/lib/msan/lit_tests/Linux/glob_nomatch.cc @@ -0,0 +1,21 @@ +// 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/syscalls.cc b/lib/msan/lit_tests/Linux/syscalls.cc index c12eda39189e..ec308bfe30ca 100644 --- a/lib/msan/lit_tests/Linux/syscalls.cc +++ b/lib/msan/lit_tests/Linux/syscalls.cc @@ -7,6 +7,10 @@ #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> @@ -16,6 +20,7 @@ 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); @@ -46,5 +51,50 @@ int main(int argc, char *argv[]) { __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 new file mode 100644 index 000000000000..e6e101db884f --- /dev/null +++ b/lib/msan/lit_tests/Linux/tcgetattr.cc @@ -0,0 +1,21 @@ +// 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 new file mode 100644 index 000000000000..8930a7159246 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/dso-origin-so.cc @@ -0,0 +1,14 @@ +#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 new file mode 100644 index 000000000000..ff926b3f61c8 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/dso-origin.h @@ -0,0 +1,4 @@ +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 new file mode 100644 index 000000000000..b3677c17a0f2 --- /dev/null +++ b/lib/msan/lit_tests/SharedLibs/lit.local.cfg @@ -0,0 +1,4 @@ +# 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.cfg b/lib/msan/lit_tests/Unit/lit.cfg deleted file mode 100644 index ee379d0deaed..000000000000 --- a/lib/msan/lit_tests/Unit/lit.cfg +++ /dev/null @@ -1,26 +0,0 @@ -# -*- Python -*- - -import os - -def get_required_attr(config, attr_name): - attr_value = getattr(config, attr_name, None) - if not attr_value: - lit.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 attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, 'compiler_rt_src_root') -compiler_rt_lit_unit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.unit.cfg") -lit.load_config(config, compiler_rt_lit_unit_cfg) - -# 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. -msan_binary_dir = get_required_attr(config, "msan_binary_dir") -config.test_exec_root = os.path.join(msan_binary_dir, "tests") -config.test_source_root = config.test_exec_root diff --git a/lib/msan/lit_tests/Unit/lit.site.cfg.in b/lib/msan/lit_tests/Unit/lit.site.cfg.in index a91f6713303a..8e67f557d7fd 100644 --- a/lib/msan/lit_tests/Unit/lit.site.cfg.in +++ b/lib/msan/lit_tests/Unit/lit.site.cfg.in @@ -1,17 +1,13 @@ ## Autogenerated by LLVM/Clang configuration. # Do not edit! -config.target_triple = "@TARGET_TRIPLE@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_build_mode = "@LLVM_BUILD_MODE@" -config.msan_binary_dir = "@MSAN_BINARY_DIR@" +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.unit.configured") -try: - config.llvm_build_mode = config.llvm_build_mode % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) +# Setup config name. +config.name = 'MemorySanitizer-Unit' -# Let the main config do the real work. -lit.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/Unit/lit.cfg") +# 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 new file mode 100644 index 000000000000..aaa85cce7113 --- /dev/null +++ b/lib/msan/lit_tests/allocator_returns_null.cc @@ -0,0 +1,81 @@ +// 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 new file mode 100644 index 000000000000..48684c29c60d --- /dev/null +++ b/lib/msan/lit_tests/backtrace.cc @@ -0,0 +1,26 @@ +// 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/cxa_atexit.cc b/lib/msan/lit_tests/cxa_atexit.cc new file mode 100644 index 000000000000..f3641aadce03 --- /dev/null +++ b/lib/msan/lit_tests/cxa_atexit.cc @@ -0,0 +1,28 @@ +// 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/dlerror.cc b/lib/msan/lit_tests/dlerror.cc new file mode 100644 index 000000000000..281b3164fd7e --- /dev/null +++ b/lib/msan/lit_tests/dlerror.cc @@ -0,0 +1,14 @@ +// 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 new file mode 100644 index 000000000000..13661c65e744 --- /dev/null +++ b/lib/msan/lit_tests/dso-origin.cc @@ -0,0 +1,25 @@ +// 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 new file mode 100644 index 000000000000..af27ad0b0329 --- /dev/null +++ b/lib/msan/lit_tests/errno.cc @@ -0,0 +1,17 @@ +// 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 index f16679cc2aa2..7fde1fdfab93 100644 --- a/lib/msan/lit_tests/getaddrinfo-positive.cc +++ b/lib/msan/lit_tests/getaddrinfo-positive.cc @@ -8,12 +8,16 @@ #include <netdb.h> #include <stdlib.h> +volatile int z; + int main(void) { struct addrinfo *ai; struct addrinfo hint; - int res = getaddrinfo("localhost", NULL, &hint, &ai); + 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: Use of uninitialized value + // 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/getline.cc b/lib/msan/lit_tests/getline.cc new file mode 100644 index 000000000000..27168a885606 --- /dev/null +++ b/lib/msan/lit_tests/getline.cc @@ -0,0 +1,30 @@ +// 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 new file mode 100644 index 000000000000..5ba1d4cec0dd --- /dev/null +++ b/lib/msan/lit_tests/getline_test_data @@ -0,0 +1,2 @@ +abcde +12345 diff --git a/lib/msan/lit_tests/heap-origin.cc b/lib/msan/lit_tests/heap-origin.cc index 54e2c31438ff..dfe7edd27e82 100644 --- a/lib/msan/lit_tests/heap-origin.cc +++ b/lib/msan/lit_tests/heap-origin.cc @@ -19,15 +19,13 @@ #include <stdlib.h> int main(int argc, char **argv) { char *volatile x = (char*)malloc(5 * sizeof(char)); - if (*x) - exit(0); - // CHECK: WARNING: Use of uninitialized value - // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-3]] + 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-8]] + // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} - return 0; } diff --git a/lib/msan/lit_tests/initgroups.cc b/lib/msan/lit_tests/initgroups.cc new file mode 100644 index 000000000000..adba5369579a --- /dev/null +++ b/lib/msan/lit_tests/initgroups.cc @@ -0,0 +1,11 @@ +// 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 new file mode 100644 index 000000000000..4aeb15583f84 --- /dev/null +++ b/lib/msan/lit_tests/inline.cc @@ -0,0 +1,20 @@ +// 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 new file mode 100644 index 000000000000..769ea45f8c4d --- /dev/null +++ b/lib/msan/lit_tests/insertvalue_origin.cc @@ -0,0 +1,35 @@ +// 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 new file mode 100644 index 000000000000..caff80c2e5d7 --- /dev/null +++ b/lib/msan/lit_tests/ioctl.cc @@ -0,0 +1,20 @@ +// 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 new file mode 100644 index 000000000000..94ed528c70b9 --- /dev/null +++ b/lib/msan/lit_tests/ioctl_custom.cc @@ -0,0 +1,33 @@ +// 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 new file mode 100644 index 000000000000..6d006756a110 --- /dev/null +++ b/lib/msan/lit_tests/keep-going-dso.cc @@ -0,0 +1,33 @@ +// 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 new file mode 100644 index 000000000000..e33b137c76f7 --- /dev/null +++ b/lib/msan/lit_tests/keep-going.cc @@ -0,0 +1,34 @@ +// 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 index 42381885fe8e..da1bde6dd04a 100644 --- a/lib/msan/lit_tests/lit.cfg +++ b/lib/msan/lit_tests/lit.cfg @@ -2,12 +2,15 @@ import os +import lit.util + def get_required_attr(config, attr_name): attr_value = getattr(config, attr_name, None) if not attr_value: - lit.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) + 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. @@ -17,9 +20,9 @@ config.name = 'MemorySanitizer' config.test_source_root = os.path.dirname(__file__) def DisplayNoConfigMessage(): - lit.fatal("No site specific configuration available! " + - "Try running your test from the build tree or running " + - "make check-msan") + 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) @@ -27,9 +30,9 @@ 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.params.get('msan_site_config', None) + msan_site_cfg = lit_config.params.get('msan_site_config', None) if (msan_site_cfg) and (os.path.exists(msan_site_cfg)): - lit.load_config(config, 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 @@ -45,25 +48,17 @@ if llvm_src_root is None: if (not msan_site_cfg) or (not os.path.exists(msan_site_cfg)): DisplayNoConfigMessage() - lit.load_config(config, msan_site_cfg) + lit_config.load_config(config, msan_site_cfg) raise SystemExit -# Setup attributes common for all compiler-rt projects. -compiler_rt_src_root = get_required_attr(config, "compiler_rt_src_root") -compiler_rt_lit_cfg = os.path.join(compiler_rt_src_root, "lib", - "lit.common.cfg") -if (not compiler_rt_lit_cfg) or (not os.path.exists(compiler_rt_lit_cfg)): - lit.fatal("Can't find common compiler-rt lit config at: %r" - % compiler_rt_lit_cfg) -lit.load_config(config, compiler_rt_lit_cfg) - # 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"] -clang_msan_cxxflags = ["-ccc-cxx "] + clang_msan_cflags + "-g", + "-m64"] +clang_msan_cxxflags = ["--driver-mode=g++ "] + clang_msan_cflags config.substitutions.append( ("%clang_msan ", " ".join([config.clang] + clang_msan_cflags) + " ") ) @@ -71,12 +66,6 @@ config.substitutions.append( ("%clangxx_msan ", " ".join([config.clang] + clang_msan_cxxflags) + " ") ) -# Setup path to external LLVM symbolizer to run MemorySanitizer output tests. -llvm_tools_dir = getattr(config, 'llvm_tools_dir', None) -if llvm_tools_dir: - llvm_symbolizer_path = os.path.join(llvm_tools_dir, "llvm-symbolizer") - config.environment['MSAN_SYMBOLIZER_PATH'] = llvm_symbolizer_path - # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] diff --git a/lib/msan/lit_tests/lit.site.cfg.in b/lib/msan/lit_tests/lit.site.cfg.in index 3b969e0b0614..946df778f3d3 100644 --- a/lib/msan/lit_tests/lit.site.cfg.in +++ b/lib/msan/lit_tests/lit.site.cfg.in @@ -1,18 +1,5 @@ -config.target_triple = "@TARGET_TRIPLE@" -config.host_os = "@HOST_OS@" -config.llvm_src_root = "@LLVM_SOURCE_DIR@" -config.compiler_rt_src_root = "@COMPILER_RT_SOURCE_DIR@" -config.llvm_obj_root = "@LLVM_BINARY_DIR@" -config.llvm_tools_dir = "@LLVM_TOOLS_DIR@" -config.clang = "@LLVM_BINARY_DIR@/bin/clang" +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/lib/lit.common.configured") -# LLVM tools dir can be passed in lit parameters, so try to -# apply substitution. -try: - config.llvm_tools_dir = config.llvm_tools_dir % lit.params -except KeyError,e: - key, = e.args - lit.fatal("unable to find %r parameter, use '--param=%s=VALUE'" % (key, key)) - -# Let the main config do the real work. -lit.load_config(config, "@MSAN_SOURCE_DIR@/lit_tests/lit.cfg") +# 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 new file mode 100644 index 000000000000..fc68fbc35fbb --- /dev/null +++ b/lib/msan/lit_tests/malloc_hook.cc @@ -0,0 +1,36 @@ +// 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_prop.cc b/lib/msan/lit_tests/no_sanitize_memory_prop.cc index c74ca6b89db9..355152478852 100644 --- a/lib/msan/lit_tests/no_sanitize_memory_prop.cc +++ b/lib/msan/lit_tests/no_sanitize_memory_prop.cc @@ -25,7 +25,7 @@ int main(void) { int x; int * volatile p = &x; int y = f(*p); - // CHECK: WARNING: Use of uninitialized value + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value // CHECK: {{#0 0x.* in main .*no_sanitize_memory_prop.cc:}}[[@LINE+1]] if (y) exit(0); diff --git a/lib/msan/lit_tests/poison_in_free.cc b/lib/msan/lit_tests/poison_in_free.cc new file mode 100644 index 000000000000..f134d05abb1e --- /dev/null +++ b/lib/msan/lit_tests/poison_in_free.cc @@ -0,0 +1,16 @@ +// 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 new file mode 100644 index 000000000000..d0e83eabd6a4 --- /dev/null +++ b/lib/msan/lit_tests/ptrace.cc @@ -0,0 +1,36 @@ +// 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/scandir.cc b/lib/msan/lit_tests/scandir.cc new file mode 100644 index 000000000000..94672e1adbee --- /dev/null +++ b/lib/msan/lit_tests/scandir.cc @@ -0,0 +1,56 @@ +// 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 new file mode 100644 index 000000000000..84af7f418d21 --- /dev/null +++ b/lib/msan/lit_tests/scandir_null.cc @@ -0,0 +1,34 @@ +// 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 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/aaa diff --git a/lib/msan/lit_tests/scandir_test_root/aab b/lib/msan/lit_tests/scandir_test_root/aab new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/aab diff --git a/lib/msan/lit_tests/scandir_test_root/bbb b/lib/msan/lit_tests/scandir_test_root/bbb new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/lib/msan/lit_tests/scandir_test_root/bbb diff --git a/lib/msan/lit_tests/select.cc b/lib/msan/lit_tests/select.cc new file mode 100644 index 000000000000..a169a2dd9118 --- /dev/null +++ b/lib/msan/lit_tests/select.cc @@ -0,0 +1,22 @@ +// 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 new file mode 100644 index 000000000000..a22b744d74db --- /dev/null +++ b/lib/msan/lit_tests/setlocale.cc @@ -0,0 +1,13 @@ +// 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 new file mode 100644 index 000000000000..ea75eae1bdaa --- /dev/null +++ b/lib/msan/lit_tests/signal_stress_test.cc @@ -0,0 +1,71 @@ +// 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 new file mode 100644 index 000000000000..29aa86c938f2 --- /dev/null +++ b/lib/msan/lit_tests/sigwait.cc @@ -0,0 +1,30 @@ +// 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 new file mode 100644 index 000000000000..d4f004598a62 --- /dev/null +++ b/lib/msan/lit_tests/sigwaitinfo.cc @@ -0,0 +1,31 @@ +// 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 index 90f527309224..b0b05d9658bf 100644 --- a/lib/msan/lit_tests/stack-origin.cc +++ b/lib/msan/lit_tests/stack-origin.cc @@ -20,13 +20,12 @@ int main(int argc, char **argv) { int x; int *volatile p = &x; - if (*p) - exit(0); - // CHECK: WARNING: Use of uninitialized value - // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-3]] + 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}} - return 0; } diff --git a/lib/msan/lit_tests/sync_lock_set_and_test.cc b/lib/msan/lit_tests/sync_lock_set_and_test.cc new file mode 100644 index 000000000000..1023b3e54368 --- /dev/null +++ b/lib/msan/lit_tests/sync_lock_set_and_test.cc @@ -0,0 +1,7 @@ +// 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 new file mode 100644 index 000000000000..7e1c2cfad566 --- /dev/null +++ b/lib/msan/lit_tests/tzset.cc @@ -0,0 +1,16 @@ +// 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 new file mode 100644 index 000000000000..fa29ab69de1b --- /dev/null +++ b/lib/msan/lit_tests/unaligned_read_origin.cc @@ -0,0 +1,16 @@ +// 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 new file mode 100644 index 000000000000..ac47c0233a10 --- /dev/null +++ b/lib/msan/lit_tests/use-after-free.cc @@ -0,0 +1,34 @@ +// 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 new file mode 100644 index 000000000000..c200c77de96a --- /dev/null +++ b/lib/msan/lit_tests/vector_cvt.cc @@ -0,0 +1,23 @@ +// 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 new file mode 100644 index 000000000000..e8d55423293c --- /dev/null +++ b/lib/msan/lit_tests/vector_select.cc @@ -0,0 +1,13 @@ +// 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 new file mode 100644 index 000000000000..b4bac1ecbd22 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls.cc @@ -0,0 +1,64 @@ +// 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 new file mode 100644 index 000000000000..a0af8b7bb0c5 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/caller.cc @@ -0,0 +1,51 @@ +// 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 new file mode 100644 index 000000000000..5e01230c0986 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/lit.local.cfg @@ -0,0 +1,3 @@ +# 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 new file mode 100644 index 000000000000..ab7bf4125c0a --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/one.cc @@ -0,0 +1,3 @@ +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 new file mode 100644 index 000000000000..c939a993bc9a --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/two.cc @@ -0,0 +1,11 @@ +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 new file mode 100644 index 000000000000..8fcd0c635d96 --- /dev/null +++ b/lib/msan/lit_tests/wrap_indirect_calls/wrapper.cc @@ -0,0 +1,11 @@ +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 aa79b31be2e0..83b11e5c2ff3 100644 --- a/lib/msan/msan.cc +++ b/lib/msan/msan.cc @@ -58,14 +58,17 @@ static THREADLOCAL struct { uptr stack_top, stack_bottom; } __msan_stack_bounds; -static THREADLOCAL bool is_in_symbolizer; -static THREADLOCAL bool is_in_loader; +static THREADLOCAL int is_in_symbolizer; +static THREADLOCAL int is_in_loader; + +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_track_origins; -extern "C" const int __msan_track_origins; int __msan_get_track_origins() { - return __msan_track_origins; + return &__msan_track_origins ? __msan_track_origins : 0; } +extern "C" SANITIZER_WEAK_ATTRIBUTE const int __msan_keep_going; + namespace __msan { static bool IsRunningUnderDr() { @@ -84,12 +87,12 @@ static bool IsRunningUnderDr() { return result; } -void EnterSymbolizer() { is_in_symbolizer = true; } -void ExitSymbolizer() { is_in_symbolizer = false; } +void EnterSymbolizer() { ++is_in_symbolizer; } +void ExitSymbolizer() { --is_in_symbolizer; } bool IsInSymbolizer() { return is_in_symbolizer; } -void EnterLoader() { is_in_loader = true; } -void ExitLoader() { is_in_loader = false; } +void EnterLoader() { ++is_in_loader; } +void ExitLoader() { --is_in_loader; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE @@ -111,6 +114,7 @@ int msan_report_count = 0; // FIXME: make it resizable. static const uptr kNumStackOriginDescrs = 1024 * 1024; static const char *StackOriginDescr[kNumStackOriginDescrs]; +static uptr StackOriginPC[kNumStackOriginDescrs]; static atomic_uint32_t NumStackOriginDescrs; static void ParseFlagsFromString(Flags *f, const char *str) { @@ -118,33 +122,39 @@ static void ParseFlagsFromString(Flags *f, const char *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); - f->exit_code = 1; Die(); } ParseFlag(str, &f->report_umrs, "report_umrs"); - ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->wrap_signals, "wrap_signals"); + + // 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"); + f->halt_on_error = !f->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(); cf->external_symbolizer_path = GetEnv("MSAN_SYMBOLIZER_PATH"); - cf->strip_path_prefix = ""; - cf->fast_unwind_on_fatal = false; - cf->fast_unwind_on_malloc = true; cf->malloc_context_size = 20; + cf->handle_ioctl = true; internal_memset(f, 0, sizeof(*f)); f->poison_heap_with_zeroes = false; f->poison_stack_with_zeroes = false; f->poison_in_malloc = true; + f->poison_in_free = true; f->exit_code = 77; f->report_umrs = true; - f->verbosity = 0; f->wrap_signals = true; + f->halt_on_error = !&__msan_keep_going; // Override from user-specified string. if (__msan_default_options) @@ -166,19 +176,15 @@ static void GetCurrentStackBounds(uptr *stack_top, uptr *stack_bottom) { } void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - bool fast) { - if (!fast) { + bool request_fast_unwind) { + if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) { // Block reports from our interceptors during _Unwind_Backtrace. SymbolizerScope sym_scope; - return stack->SlowUnwindStack(pc, max_s); + return stack->Unwind(max_s, pc, bp, 0, 0, request_fast_unwind); } - uptr stack_top, stack_bottom; GetCurrentStackBounds(&stack_top, &stack_bottom); - stack->size = 0; - stack->trace[0] = pc; - stack->max_size = max_s; - stack->FastUnwindStack(pc, bp, stack_top, stack_bottom); + stack->Unwind(max_s, pc, bp, stack_top, stack_bottom, request_fast_unwind); } void PrintWarning(uptr pc, uptr bp) { @@ -204,16 +210,55 @@ void PrintWarningWithOrigin(uptr pc, uptr bp, u32 origin) { common_flags()->fast_unwind_on_fatal); u32 report_origin = - (__msan_track_origins && OriginIsValid(origin)) ? origin : 0; + (__msan_get_track_origins() && OriginIsValid(origin)) ? origin : 0; ReportUMR(&stack, report_origin); - if (__msan_track_origins && !OriginIsValid(origin)) { - Printf(" ORIGIN: invalid (%x). Might be a bug in MemorySanitizer, " - "please report to MemorySanitizer developers.\n", - origin); + if (__msan_get_track_origins() && !OriginIsValid(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", + origin); + } +} + +void UnpoisonParam(uptr n) { + internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); +} + +// Backup MSan runtime TLS state. +// Implementation must be async-signal-safe. +// Instances of this class may live on the signal handler stack, and data size +// may be an issue. +void ScopedThreadLocalStateBackup::Backup() { + va_arg_overflow_size_tls = __msan_va_arg_overflow_size_tls; +} + +void ScopedThreadLocalStateBackup::Restore() { + // A lame implementation that only keeps essential state and resets the rest. + __msan_va_arg_overflow_size_tls = va_arg_overflow_size_tls; + + internal_memset(__msan_param_tls, 0, sizeof(__msan_param_tls)); + internal_memset(__msan_retval_tls, 0, sizeof(__msan_retval_tls)); + 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_param_origin_tls, 0, + sizeof(__msan_param_origin_tls)); } } +void UnpoisonThreadLocalState() { +} + +const char *GetOriginDescrIfStack(u32 id, uptr *pc) { + if ((id >> 31) == 0) return 0; + id &= (1U << 31) - 1; + CHECK_LT(id, kNumStackOriginDescrs); + if (pc) *pc = StackOriginPC[id]; + return StackOriginDescr[id]; +} + } // namespace __msan // Interface. @@ -224,6 +269,10 @@ void __msan_warning() { GET_CALLER_PC_BP_SP; (void)sp; PrintWarning(pc, bp); + if (__msan::flags()->halt_on_error) { + Printf("Exiting\n"); + Die(); + } } void __msan_warning_noreturn() { @@ -239,17 +288,20 @@ void __msan_init() { msan_init_is_running = 1; SanitizerToolName = "MemorySanitizer"; - InstallAtExitHandler(); SetDieCallback(MsanDie); InitTlsSize(); + + const char *msan_options = GetEnv("MSAN_OPTIONS"); + InitializeFlags(&msan_flags, msan_options); + __sanitizer_set_report_path(common_flags()->log_path); + InitializeInterceptors(); + InstallAtExitHandler(); // Needs __cxa_atexit interceptor. if (MSAN_REPLACE_OPERATORS_NEW_AND_DELETE) ReplaceOperatorsNewAndDelete(); - const char *msan_options = GetEnv("MSAN_OPTIONS"); - InitializeFlags(&msan_flags, msan_options); if (StackSizeIsUnlimited()) { - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("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. @@ -257,15 +309,15 @@ void __msan_init() { ReExec(); } - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("MSAN_OPTIONS: %s\n", msan_options ? msan_options : "<empty>"); msan_running_under_dr = IsRunningUnderDr(); __msan_clear_on_return(); - if (__msan_track_origins && flags()->verbosity > 0) + if (__msan_get_track_origins() && common_flags()->verbosity > 0) Printf("msan_track_origins\n"); - if (!InitShadow(/* prot1 */false, /* prot2 */true, /* map_shadow */true, - __msan_track_origins)) { + if (!InitShadow(/* prot1 */ false, /* prot2 */ true, /* map_shadow */ true, + __msan_get_track_origins())) { // FIXME: prot1 = false is only required when running under DR. Printf("FATAL: MemorySanitizer can not mmap the shadow memory.\n"); Printf("FATAL: Make sure to compile with -fPIE and to link with -pie.\n"); @@ -277,14 +329,17 @@ void __msan_init() { } 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(InitializeExternalSymbolizer(external_symbolizer)); + CHECK(external_symbolizer_started); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); GetThreadStackTopAndBottom(/* at_initialization */true, &__msan_stack_bounds.stack_top, &__msan_stack_bounds.stack_bottom); - if (flags()->verbosity) + if (common_flags()->verbosity) Printf("MemorySanitizer init done\n"); msan_init_is_running = 0; msan_inited = 1; @@ -294,6 +349,10 @@ void __msan_set_exit_code(int exit_code) { flags()->exit_code = exit_code; } +void __msan_set_keep_going(int keep_going) { + flags()->halt_on_error = !keep_going; +} + void __msan_set_expect_umr(int expect_umr) { if (expect_umr) { msan_expected_umr_found = 0; @@ -310,13 +369,17 @@ void __msan_set_expect_umr(int expect_umr) { } void __msan_print_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++) { Printf("%x%x ", s[i] >> 4, s[i] & 0xf); } Printf("\n"); - if (__msan_track_origins) { + if (__msan_get_track_origins()) { for (uptr i = 0; i < size / 4; i++) { Printf(" o: %x ", o[i]); } @@ -331,10 +394,6 @@ void __msan_print_param_shadow() { Printf("\n"); } -void __msan_unpoison_param(uptr n) { - internal_memset(__msan_param_tls, 0, n * sizeof(*__msan_param_tls)); -} - sptr __msan_test_shadow(const void *x, uptr size) { unsigned char *s = (unsigned char*)MEM_TO_SHADOW((uptr)x); for (uptr i = 0; i < size; ++i) @@ -396,7 +455,7 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { // Origin mapping is 4 bytes per 4 bytes of application memory. // Here we extend the range such that its left and right bounds are both // 4 byte aligned. - if (!__msan_track_origins) return; + if (!__msan_get_track_origins()) return; uptr x = MEM_TO_ORIGIN((uptr)a); uptr beg = x & ~3UL; // align down. uptr end = (x + size + 3) & ~3UL; // align up. @@ -417,6 +476,10 @@ void __msan_set_origin(const void *a, uptr size, u32 origin) { // 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) { + __msan_set_alloca_origin4(a, size, descr, 0); +} + +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc) { static const u32 dash = '-'; static const u32 first_timer = dash + (dash << 8) + (dash << 16) + (dash << 24); @@ -429,8 +492,9 @@ void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { *id_ptr = id; CHECK_LT(id, kNumStackOriginDescrs); StackOriginDescr[id] = descr + 4; + StackOriginPC[id] = pc; if (print) - Printf("First time: id=%d %s \n", id, descr + 4); + Printf("First time: id=%d %s %p \n", id, descr + 4, pc); } id |= 1U << 31; if (print) @@ -439,15 +503,11 @@ void __msan_set_alloca_origin(void *a, uptr size, const char *descr) { } const char *__msan_get_origin_descr_if_stack(u32 id) { - if ((id >> 31) == 0) return 0; - id &= (1U << 31) - 1; - CHECK_LT(id, kNumStackOriginDescrs); - return StackOriginDescr[id]; + return GetOriginDescrIfStack(id, 0); } - u32 __msan_get_origin(const void *a) { - if (!__msan_track_origins) return 0; + if (!__msan_get_track_origins()) return 0; uptr x = (uptr)a; uptr aligned = x & ~3ULL; uptr origin_ptr = MEM_TO_ORIGIN(aligned); @@ -458,9 +518,46 @@ u32 __msan_get_umr_origin() { return __msan_origin_tls; } +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); + 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); + 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); + 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]; + *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]; + *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]; + *p = x; +} + #if !SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char* __msan_default_options() { return ""; } } // extern "C" #endif diff --git a/lib/msan/msan.h b/lib/msan/msan.h index baaba49f4187..4e6c6194505e 100644 --- a/lib/msan/msan.h +++ b/lib/msan/msan.h @@ -25,13 +25,12 @@ # define MSAN_REPLACE_OPERATORS_NEW_AND_DELETE 1 #endif -#define MEM_TO_SHADOW(mem) (((uptr)mem) & ~0x400000000000ULL) -#define MEM_TO_ORIGIN(mem) (MEM_TO_SHADOW(mem) + 0x200000000000ULL) -#define MEM_IS_APP(mem) ((uptr)mem >= 0x600000000000ULL) -#define MEM_IS_SHADOW(mem) ((uptr)mem >= 0x200000000000ULL && \ - (uptr)mem <= 0x400000000000ULL) - -struct link_map; // Opaque type returned by dlopen(). +#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) +#define MEM_IS_SHADOW(mem) \ + ((uptr)mem >= 0x200000000000ULL && (uptr)mem <= 0x400000000000ULL) const int kMsanParamTlsSizeInWords = 100; const int kMsanRetvalTlsSizeInWords = 100; @@ -46,13 +45,16 @@ bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins); char *GetProcSelfMaps(); void InitializeInterceptors(); +void MsanAllocatorThreadFinish(); void *MsanReallocate(StackTrace *stack, void *oldp, uptr size, uptr alignment, bool zeroise); -void MsanDeallocate(void *ptr); +void MsanDeallocate(StackTrace *stack, void *ptr); void InstallTrapHandler(); void InstallAtExitHandler(); void ReplaceOperatorsNewAndDelete(); +const char *GetOriginDescrIfStack(u32 id, uptr *pc); + void EnterSymbolizer(); void ExitSymbolizer(); bool IsInSymbolizer(); @@ -70,13 +72,15 @@ 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, - bool fast); + bool request_fast_unwind); void ReportUMR(StackTrace *stack, u32 origin); void ReportExpectedUMRNotFound(StackTrace *stack); void ReportAtExitStatistics(); -void UnpoisonMappedDSO(struct link_map *map); +// Unpoison first n function arguments. +void UnpoisonParam(uptr n); +void UnpoisonThreadLocalState(); #define GET_MALLOC_STACK_TRACE \ StackTrace stack; \ @@ -86,6 +90,20 @@ void UnpoisonMappedDSO(struct link_map *map); StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \ common_flags()->fast_unwind_on_malloc) +class ScopedThreadLocalStateBackup { + public: + ScopedThreadLocalStateBackup() { Backup(); } + ~ScopedThreadLocalStateBackup() { Restore(); } + void Backup(); + void Restore(); + private: + u64 va_arg_overflow_size_tls; +}; } // namespace __msan +#define MSAN_MALLOC_HOOK(ptr, size) \ + if (&__msan_malloc_hook) __msan_malloc_hook(ptr, size) +#define MSAN_FREE_HOOK(ptr) \ + if (&__msan_free_hook) __msan_free_hook(ptr) + #endif // MSAN_H diff --git a/lib/msan/msan.syms b/lib/msan/msan.syms deleted file mode 100644 index 24bbaba478b7..000000000000 --- a/lib/msan/msan.syms +++ /dev/null @@ -1,5 +0,0 @@ -{ - __msan_*; - __sanitizer_syscall_pre_*; - __sanitizer_syscall_post_*; -}; diff --git a/lib/msan/msan.syms.extra b/lib/msan/msan.syms.extra new file mode 100644 index 000000000000..aad41cf1124e --- /dev/null +++ b/lib/msan/msan.syms.extra @@ -0,0 +1 @@ +__msan_* diff --git a/lib/msan/msan_allocator.cc b/lib/msan/msan_allocator.cc index 7435843ce61e..2badf712188b 100644 --- a/lib/msan/msan_allocator.cc +++ b/lib/msan/msan_allocator.cc @@ -25,6 +25,7 @@ struct Metadata { 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> PrimaryAllocator; @@ -45,35 +46,56 @@ static inline void Init() { allocator.Init(); } +void MsanAllocatorThreadFinish() { + allocator.SwallowCache(&cache); +} + 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)); meta->requested_size = size; - if (zeroise) + if (zeroise) { __msan_clear_and_unpoison(res, size); - else if (flags()->poison_in_malloc) + } else if (flags()->poison_in_malloc) { __msan_poison(res, 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); + 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); + } } + MSAN_MALLOC_HOOK(res, size); return res; } -void MsanDeallocate(void *p) { +void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); Init(); + MSAN_FREE_HOOK(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 // poisoned. - __msan_poison(p, size); - if (__msan_get_track_origins()) - __msan_set_origin(p, size, -1); + 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); + } + } allocator.Deallocate(&cache, p); } @@ -82,7 +104,7 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, if (!old_p) return MsanAllocate(stack, new_size, alignment, zeroise); if (!new_size) { - MsanDeallocate(old_p); + MsanDeallocate(stack, old_p); return 0; } Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p)); @@ -98,10 +120,59 @@ void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, uptr memcpy_size = Min(new_size, old_size); void *new_p = MsanAllocate(stack, new_size, alignment, zeroise); // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size); - if (new_p) + if (new_p) { __msan_memcpy(new_p, old_p, memcpy_size); - MsanDeallocate(old_p); + MsanDeallocate(stack, old_p); + } return new_p; } +static uptr AllocationSize(const void *p) { + if (p == 0) + return 0; + const void *beg = allocator.GetBlockBegin(p); + if (beg != p) + return 0; + Metadata *b = (Metadata*)allocator.GetMetaData(p); + return b->requested_size; +} + } // namespace __msan + +using namespace __msan; + +uptr __msan_get_current_allocated_bytes() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMalloced]; + u64 f = stats[AllocatorStatFreed]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_heap_size() { + u64 stats[AllocatorStatCount]; + allocator.GetStats(stats); + u64 m = stats[AllocatorStatMmapped]; + u64 f = stats[AllocatorStatUnmapped]; + return m >= f ? m - f : 1; +} + +uptr __msan_get_free_bytes() { + return 1; +} + +uptr __msan_get_unmapped_bytes() { + return 1; +} + +uptr __msan_get_estimated_allocated_size(uptr size) { + return size; +} + +int __msan_get_ownership(const void *p) { + return AllocationSize(p) != 0; +} + +uptr __msan_get_allocated_size(const void *p) { + return AllocationSize(p); +} diff --git a/lib/msan/msan_flags.h b/lib/msan/msan_flags.h index 64ef84509888..93fa8a60dba0 100644 --- a/lib/msan/msan_flags.h +++ b/lib/msan/msan_flags.h @@ -19,12 +19,13 @@ namespace __msan { // Flags. struct Flags { int exit_code; - int verbosity; 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 halt_on_error; }; Flags *flags(); diff --git a/lib/msan/msan_interceptors.cc b/lib/msan/msan_interceptors.cc index 1bcf93db9440..15a8bec12f31 100644 --- a/lib/msan/msan_interceptors.cc +++ b/lib/msan/msan_interceptors.cc @@ -19,6 +19,8 @@ #include "msan.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" +#include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_libc.h" @@ -28,10 +30,15 @@ // ACHTUNG! No other system header includes in this file. // Ideally, we should get rid of stdarg.h as well. -extern "C" const int __msan_keep_going; - using namespace __msan; +using __sanitizer::memory_order; +using __sanitizer::atomic_load; +using __sanitizer::atomic_store; +using __sanitizer::atomic_uintptr_t; + +static unsigned g_thread_finalize_key; + // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; @@ -52,28 +59,30 @@ bool IsInInterceptorScope() { } while (0) // Check that [x, x+n) range is unpoisoned. -#define CHECK_UNPOISONED_0(x, n) \ - do { \ - sptr offset = __msan_test_shadow(x, n); \ - if (__msan::IsInSymbolizer()) break; \ - if (offset >= 0 && __msan::flags()->report_umrs) { \ - GET_CALLER_PC_BP_SP; \ - (void) sp; \ - Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ - offset, x, n); \ - __msan::PrintWarningWithOrigin(pc, bp, \ - __msan_get_origin((char *) x + offset)); \ - if (!__msan_keep_going) { \ - Printf("Exiting\n"); \ - Die(); \ - } \ - } \ +#define CHECK_UNPOISONED_0(x, n) \ + do { \ + sptr offset = __msan_test_shadow(x, n); \ + if (__msan::IsInSymbolizer()) break; \ + if (offset >= 0 && __msan::flags()->report_umrs) { \ + GET_CALLER_PC_BP_SP; \ + (void) sp; \ + Printf("UMR in %s at offset %d inside [%p, +%d) \n", __FUNCTION__, \ + offset, x, n); \ + __msan::PrintWarningWithOrigin(pc, bp, \ + __msan_get_origin((char *)x + offset)); \ + if (__msan::flags()->halt_on_error) { \ + Printf("Exiting\n"); \ + Die(); \ + } \ + } \ } while (0) // Check that [x, x+n) range is unpoisoned unless we are in a nested // interceptor. -#define CHECK_UNPOISONED(x, n) \ - if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED(x, n) \ + do { \ + if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ + } while (0); static void *fast_memset(void *ptr, int c, SIZE_T n); static void *fast_memcpy(void *dst, const void *src, SIZE_T n); @@ -103,22 +112,22 @@ INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { return res; } -INTERCEPTOR(void *, readdir, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent_sz); - return res; +INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { + return __msan_memcpy(dest, src, n); } -INTERCEPTOR(void *, readdir64, void *a) { - ENSURE_MSAN_INITED(); - void *res = REAL(readdir)(a); - __msan_unpoison(res, __sanitizer::struct_dirent64_sz); - return res; +INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { + return (char *)__msan_memcpy(dest, src, n) + n; } -INTERCEPTOR(void *, memcpy, void *dest, const void *src, SIZE_T n) { - return __msan_memcpy(dest, src, n); +INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { + ENSURE_MSAN_INITED(); + void *res = REAL(memccpy)(dest, src, c, n); + CHECK(!res || (res >= dest && res <= (char *)dest + n)); + SIZE_T sz = res ? (char *)res - (char *)dest : n; + CHECK_UNPOISONED(src, sz); + __msan_unpoison(dest, sz); + return res; } INTERCEPTOR(void *, memmove, void *dest, const void *src, SIZE_T n) { @@ -129,6 +138,10 @@ INTERCEPTOR(void *, memset, void *s, int c, SIZE_T n) { return __msan_memset(s, c, n); } +INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { + return __msan_memmove(dest, src, n); +} + INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); @@ -139,10 +152,35 @@ INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { return 0; } +INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + CHECK_EQ(boundary & (boundary - 1), 0); + void *ptr = MsanReallocate(&stack, 0, size, boundary, false); + return ptr; +} + +INTERCEPTOR(void *, valloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + void *ptr = MsanReallocate(&stack, 0, size, GetPageSizeCached(), false); + return ptr; +} + +INTERCEPTOR(void *, pvalloc, SIZE_T size) { + GET_MALLOC_STACK_TRACE; + uptr PageSize = GetPageSizeCached(); + size = RoundUpTo(size, PageSize); + if (size == 0) { + // pvalloc(0) should allocate one page. + size = PageSize; + } + void *ptr = MsanReallocate(&stack, 0, size, PageSize, false); + return ptr; +} + INTERCEPTOR(void, free, void *ptr) { - ENSURE_MSAN_INITED(); + GET_MALLOC_STACK_TRACE; if (ptr == 0) return; - MsanDeallocate(ptr); + MsanDeallocate(&stack, ptr); } INTERCEPTOR(SIZE_T, strlen, const char *s) { @@ -181,6 +219,14 @@ INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT return res; } +INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT + ENSURE_MSAN_INITED(); + SIZE_T n = REAL(strlen)(src); + char *res = REAL(stpcpy)(dest, src); // NOLINT + __msan_copy_poison(dest, src, n + 1); + return res; +} + INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); SIZE_T n = REAL(strlen)(src); @@ -295,6 +341,26 @@ INTERCEPTOR(double, strtod, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(double, strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(double, __strtod_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + double res = REAL(__strtod_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); float res = REAL(strtof)(nptr, endptr); // NOLINT @@ -304,6 +370,26 @@ INTERCEPTOR(float, strtof, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(float, strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(float, __strtof_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + float res = REAL(__strtof_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT ENSURE_MSAN_INITED(); long double res = REAL(strtold)(nptr, endptr); // NOLINT @@ -313,11 +399,50 @@ INTERCEPTOR(long double, strtold, const char *nptr, char **endptr) { // NOLINT return res; } +INTERCEPTOR(long double, strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(long double, __strtold_l, const char *nptr, char **endptr, + void *loc) { // NOLINT + ENSURE_MSAN_INITED(); + long double res = REAL(__strtold_l)(nptr, endptr, loc); // NOLINT + if (!__msan_has_dynamic_component()) { + __msan_unpoison(endptr, sizeof(*endptr)); + } + return res; +} + +INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) { + ENSURE_MSAN_INITED(); + int res = REAL(vasprintf)(strp, format, ap); + if (res >= 0 && !__msan_has_dynamic_component()) { + __msan_unpoison(strp, sizeof(*strp)); + __msan_unpoison(*strp, res + 1); + } + return res; +} + +INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) { // NOLINT + ENSURE_MSAN_INITED(); + va_list ap; + va_start(ap, format); + int res = vasprintf(strp, format, ap); // NOLINT + va_end(ap); + return res; +} + INTERCEPTOR(int, vsnprintf, char *str, uptr size, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsnprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -326,7 +451,7 @@ INTERCEPTOR(int, vsnprintf, char *str, uptr size, INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vsprintf)(str, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, res + 1); } return res; @@ -335,7 +460,7 @@ INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) { INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vswprintf)(str, size, format, ap); - if (!__msan_has_dynamic_component()) { + if (res >= 0 && !__msan_has_dynamic_component()) { __msan_unpoison(str, 4 * (res + 1)); } return res; @@ -370,25 +495,24 @@ INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { // SIZE_T strftime(char *s, SIZE_T max, const char *format,const struct tm *tm); INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, - void *tm) { + __sanitizer_tm *tm) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(strftime)(s, max, format, tm); if (res) __msan_unpoison(s, res + 1); return res; } -INTERCEPTOR(SIZE_T, wcstombs, void *dest, void *src, SIZE_T size) { +INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(wcstombs)(dest, src, size); - if (res != (SIZE_T)-1) __msan_unpoison(dest, res + 1); + int res = REAL(mbtowc)(dest, src, n); + if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } -// SIZE_T mbstowcs(wchar_t *dest, const char *src, SIZE_T n); -INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T n) { +INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { ENSURE_MSAN_INITED(); - SIZE_T res = REAL(mbstowcs)(dest, src, n); - if (res != (SIZE_T)-1) __msan_unpoison(dest, (res + 1) * sizeof(wchar_t)); + SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); + if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } @@ -422,6 +546,13 @@ INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { return res; } +INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wmempcpy)(dest, src, n); + __msan_copy_poison(dest, src, n * sizeof(wchar_t)); + return res; +} + INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); @@ -450,28 +581,6 @@ INTERCEPTOR(double, wcstod, const wchar_t *nptr, wchar_t **endptr) { return res; } -// #define UNSUPPORTED(name) \ -// INTERCEPTOR(void, name, void) { \ -// Printf("MSAN: Unsupported %s\n", __FUNCTION__); \ -// Die(); \ -// } - -// FIXME: intercept the following functions: -// Note, they only matter when running without a dynamic tool. -// UNSUPPORTED(wcscoll_l) -// UNSUPPORTED(wcsnrtombs) -// UNSUPPORTED(wcstol) -// UNSUPPORTED(wcstoll) -// UNSUPPORTED(wcstold) -// UNSUPPORTED(wcstoul) -// UNSUPPORTED(wcstoull) -// UNSUPPORTED(wcsxfrm_l) -// UNSUPPORTED(wcsdup) -// UNSUPPORTED(wcsftime) -// UNSUPPORTED(wcsstr) -// UNSUPPORTED(wcsrchr) -// UNSUPPORTED(wctob) - INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); @@ -502,6 +611,32 @@ INTERCEPTOR(char *, getenv, char *name) { return res; } +extern char **environ; + +static void UnpoisonEnviron() { + char **envp = environ; + for (; *envp; ++envp) { + __msan_unpoison(envp, sizeof(*envp)); + __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); + } + // Trailing NULL pointer. + __msan_unpoison(envp, sizeof(*envp)); +} + +INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { + ENSURE_MSAN_INITED(); + int res = REAL(setenv)(name, value, overwrite); + if (!res) UnpoisonEnviron(); + return res; +} + +INTERCEPTOR(int, putenv, char *string) { + ENSURE_MSAN_INITED(); + int res = REAL(putenv)(string); + if (!res) UnpoisonEnviron(); + return res; +} + INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); @@ -518,6 +653,22 @@ INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { return res; } +INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); + return res; +} + +INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, + int flags) { + ENSURE_MSAN_INITED(); + int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); + if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); + return res; +} + INTERCEPTOR(int, __xstat, int magic, char *path, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__xstat)(magic, path, buf); @@ -592,22 +743,6 @@ INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { return res; } -INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { - ENSURE_MSAN_INITED(); - char *res = REAL(getcwd)(buf, size); - if (res) - __msan_unpoison(res, REAL(strlen)(res) + 1); - return res; -} - -INTERCEPTOR(char *, realpath, char *path, char *abspath) { - ENSURE_MSAN_INITED(); - char *res = REAL(realpath)(path, abspath); - if (res) - __msan_unpoison(abspath, REAL(strlen)(abspath) + 1); - return res; -} - INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit)(resource, rlim); @@ -628,38 +763,6 @@ INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { return res; } -INTERCEPTOR(int, statfs, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, fstatfs, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs_sz); - return res; -} - -INTERCEPTOR(int, statfs64, const char *s, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(statfs64)(s, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - -INTERCEPTOR(int, fstatfs64, int fd, void *buf) { - ENSURE_MSAN_INITED(); - int res = REAL(fstatfs64)(fd, buf); - if (!res) - __msan_unpoison(buf, __sanitizer::struct_statfs64_sz); - return res; -} - INTERCEPTOR(int, uname, void *utsname) { ENSURE_MSAN_INITED(); int res = REAL(uname)(utsname); @@ -710,35 +813,24 @@ INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, - void *srcaddr, void *addrlen) { + void *srcaddr, int *addrlen) { ENSURE_MSAN_INITED(); SIZE_T srcaddr_sz; - if (srcaddr) - srcaddr_sz = __sanitizer_get_socklen_t(addrlen); + if (srcaddr) srcaddr_sz = *addrlen; SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { __msan_unpoison(buf, res); if (srcaddr) { - SIZE_T sz = __sanitizer_get_socklen_t(addrlen); + SIZE_T sz = *addrlen; __msan_unpoison(srcaddr, (sz < srcaddr_sz) ? sz : srcaddr_sz); } } return res; } -INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct msghdr *msg, int flags) { - ENSURE_MSAN_INITED(); - SSIZE_T res = REAL(recvmsg)(fd, msg, flags); - if (res > 0) { - for (SIZE_T i = 0; i < __sanitizer_get_msghdr_iovlen(msg); ++i) - __msan_unpoison(__sanitizer_get_msghdr_iov_iov_base(msg, i), - __sanitizer_get_msghdr_iov_iov_len(msg, i)); - } - return res; -} - INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; + if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + return AllocatorReturnNull(); GET_MALLOC_STACK_TRACE; if (!msan_inited) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. @@ -814,6 +906,13 @@ INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { return res; } +INTERCEPTOR(char *, dlerror) { + ENSURE_MSAN_INITED(); + char *res = REAL(dlerror)(); + if (res != 0) __msan_unpoison(res, REAL(strlen)(res) + 1); + return res; +} + // dlopen() ultimately calls mmap() down inside the loader, which generally // doesn't participate in dynamic symbol resolution. Therefore we won't // intercept its calls to mmap, and we have to hook it here. The loader @@ -828,7 +927,7 @@ INTERCEPTOR(void *, dlopen, const char *filename, int flag) { if (!__msan_has_dynamic_component() && map) { // If msandr didn't clear the shadow before the initializers ran, we do it // ourselves afterwards. - UnpoisonMappedDSO(map); + ForEachMappedRegion(map, __msan_unpoison); } return (void *)map; } @@ -848,7 +947,7 @@ static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; - __msan_unpoison_param(3); + UnpoisonParam(3); return cbdata->callback(info, size, cbdata->data); } @@ -872,22 +971,32 @@ INTERCEPTOR(int, getrusage, int who, void *usage) { return res; } +// sigactions_mu guarantees atomicity of sigaction() and signal() calls. +// Access to sigactions[] is gone with relaxed atomics to avoid data race with +// the signal handler. const int kMaxSignals = 1024; -static uptr sigactions[kMaxSignals]; +static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(1); + typedef void (*signal_cb)(int x); - signal_cb cb = (signal_cb)sigactions[signo]; + signal_cb cb = + (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo); } static void SignalAction(int signo, void *si, void *uc) { - __msan_unpoison(si, __sanitizer::struct_sigaction_sz); + ScopedThreadLocalStateBackup stlsb; + UnpoisonParam(3); + __msan_unpoison(si, sizeof(__sanitizer_sigaction)); __msan_unpoison(uc, __sanitizer::ucontext_t_sz); typedef void (*sigaction_cb)(int, void *, void *); - sigaction_cb cb = (sigaction_cb)sigactions[signo]; + sigaction_cb cb = + (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo, si, uc); } @@ -900,25 +1009,25 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, if (flags()->wrap_signals) { SpinMutexLock lock(&sigactions_mu); CHECK_LT(signo, kMaxSignals); - uptr old_cb = sigactions[signo]; + uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : 0; if (act) { - internal_memcpy(pnew_act, act, __sanitizer::struct_sigaction_sz); - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(pnew_act); - uptr new_cb = - __sanitizer::__sanitizer_get_sigaction_sa_siginfo(pnew_act) ? - (uptr)SignalAction : (uptr)SignalHandler; + internal_memcpy(pnew_act, act, sizeof(__sanitizer_sigaction)); + uptr cb = (uptr)pnew_act->sa_sigaction; + uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) + ? (uptr)SignalAction + : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(pnew_act, new_cb); + atomic_store(&sigactions[signo], cb, memory_order_relaxed); + pnew_act->sa_sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { - uptr cb = __sanitizer::__sanitizer_get_sigaction_sa_sigaction(oldact); + uptr cb = (uptr)oldact->sa_sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - __sanitizer::__sanitizer_set_sigaction_sa_sigaction(oldact, old_cb); + oldact->sa_sigaction = (void (*)(int, void *, void *))old_cb; } } } else { @@ -926,7 +1035,7 @@ INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, } if (res == 0 && oldact) { - __msan_unpoison(oldact, __sanitizer::struct_sigaction_sz); + __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); } return res; } @@ -937,7 +1046,7 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { CHECK_LT(signo, kMaxSignals); SpinMutexLock lock(&sigactions_mu); if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { - sigactions[signo] = cb; + atomic_store(&sigactions[signo], cb, memory_order_relaxed); cb = (uptr) SignalHandler; } return REAL(signal)(signo, cb); @@ -948,8 +1057,39 @@ INTERCEPTOR(int, signal, int signo, uptr cb) { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstack(void *attr, uptr *stack, uptr *stacksize); +extern "C" int pthread_setspecific(unsigned key, const void *v); +extern "C" int pthread_yield(); + +static void thread_finalize(void *v) { + uptr iter = (uptr)v; + if (iter > 1) { + if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + return; + } + MsanAllocatorThreadFinish(); +} + +struct ThreadParam { + void* (*callback)(void *arg); + void *param; + atomic_uintptr_t done; +}; + +static void *MsanThreadStartFunc(void *arg) { + ThreadParam *p = (ThreadParam *)arg; + void* (*callback)(void *arg) = p->callback; + void *param = p->param; + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { + Printf("MemorySanitizer: failed to set thread key\n"); + Die(); + } + atomic_store(&p->done, 1, memory_order_release); + return callback(param); +} INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { @@ -960,9 +1100,19 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), attr = &myattr; } - AdjustStackSizeLinux(attr, flags()->verbosity); + AdjustStackSizeLinux(attr); + + ThreadParam p; + p.callback = callback; + p.param = param; + atomic_store(&p.done, 0, memory_order_relaxed); + + int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, (void *)&p); + if (res == 0) { + while (atomic_load(&p.done, memory_order_acquire) != 1) + pthread_yield(); + } - int res = REAL(pthread_create)(th, attr, callback, param); if (attr == &myattr) pthread_attr_destroy(&myattr); if (!res) { @@ -971,37 +1121,177 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), return res; } +INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, + void (*dtor)(void *value)) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_key_create)(key, dtor); + if (!res && key) + __msan_unpoison(key, sizeof(*key)); + return res; +} + +INTERCEPTOR(int, pthread_join, void *th, void **retval) { + ENSURE_MSAN_INITED(); + int res = REAL(pthread_join)(th, retval); + if (!res && retval) + __msan_unpoison(retval, sizeof(*retval)); + return res; +} + +extern char *tzname[2]; + +INTERCEPTOR(void, tzset) { + ENSURE_MSAN_INITED(); + REAL(tzset)(); + if (tzname[0]) + __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); + if (tzname[1]) + __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); + return; +} + +struct MSanAtExitRecord { + void (*func)(void *arg); + void *arg; +}; + +void MSanAtExitWrapper(void *arg) { + UnpoisonParam(1); + MSanAtExitRecord *r = (MSanAtExitRecord *)arg; + r->func(r->arg); + InternalFree(r); +} + +// Unpoison argument shadow for C++ module destructors. +INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, + void *dso_handle) { + if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); + ENSURE_MSAN_INITED(); + MSanAtExitRecord *r = + (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); + r->func = func; + r->arg = arg; + return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); +} + +DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) + +INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { + ENSURE_MSAN_INITED(); + void *p = REAL(shmat)(shmid, shmaddr, shmflg); + if (p != (void *)-1) { + __sanitizer_shmid_ds ds; + int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); + if (!res) { + __msan_unpoison(p, ds.shm_segsz); + } + } + return p; +} + +// Linux kernel has a bug that leads to kernel deadlock if a process +// maps TBs of memory and then calls mlock(). +static void MlockIsUnsupported() { + static atomic_uint8_t printed; + if (atomic_exchange(&printed, 1, memory_order_relaxed)) + return; + if (common_flags()->verbosity > 0) + Printf("INFO: MemorySanitizer ignores mlock/mlockall/munlock/munlockall\n"); +} + +INTERCEPTOR(int, mlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlock, const void *addr, uptr len) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, mlockall, int flags) { + MlockIsUnsupported(); + return 0; +} + +INTERCEPTOR(int, munlockall, void) { + MlockIsUnsupported(); + return 0; +} + struct MSanInterceptorContext { bool in_interceptor_scope; }; -// A version of CHECK_UNPOISED using a saved scope value. Used in common +namespace __msan { + +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + +} // namespace __msan + +extern "C" int *__errno_location(void); + +// A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. -#define CHECK_UNPOISONED_CTX(ctx, x, n) \ - if (!((MSanInterceptorContext *) ctx)->in_interceptor_scope) \ - CHECK_UNPOISONED_0(x, n); +#define CHECK_UNPOISONED_CTX(ctx, x, n) \ + do { \ + if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ + CHECK_UNPOISONED_0(x, n); \ + } while (0) +#define MSAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("MemorySanitizer: failed to intercept '" #name "'\n"); \ + } while (0) + +#define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) +#define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ + UnpoisonParam(count) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ - CHECK_UNPOISONED_CTX(ctx, ptr, size); -#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ - if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ - MSanInterceptorContext msan_ctx = { IsInInterceptorScope() }; \ - ctx = (void *)&msan_ctx; \ - InterceptorScope interceptor_scope; \ + CHECK_UNPOISONED_CTX(ctx, ptr, size) +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, ptr, size) \ + __msan_unpoison(ptr, size) +#define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ + if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ + MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ + ctx = (void *)&msan_ctx; \ + (void)ctx; \ + InterceptorScope interceptor_scope; \ + __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ \ ENSURE_MSAN_INITED(); #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) -#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) do { } while (false) +#define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ + do { \ + } while (false) +#define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ - do { } while (false) // FIXME + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) // FIXME +#define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) -#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) -#define COMMON_SYSCALL_POST_READ_RANGE(p, s) +#define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ + do { \ + } while (false) +#define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ + do { \ + } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" @@ -1062,15 +1352,41 @@ void __msan_clear_and_unpoison(void *a, uptr size) { fast_memset((void*)MEM_TO_SHADOW((uptr)a), 0, size); } +u32 get_origin_if_poisoned(uptr a, uptr size) { + unsigned char *s = (unsigned char *)MEM_TO_SHADOW(a); + for (uptr i = 0; i < size; ++i) + if (s[i]) + return *(u32 *)SHADOW_TO_ORIGIN((s + i) & ~3UL); + return 0; +} + void __msan_copy_origin(void *dst, const void *src, uptr size) { if (!__msan_get_track_origins()) return; if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; - uptr d = MEM_TO_ORIGIN(dst); - uptr s = MEM_TO_ORIGIN(src); - uptr beg = d & ~3UL; // align down. - uptr end = (d + size + 3) & ~3UL; // align up. - s = s & ~3UL; // align down. - fast_memcpy((void*)beg, (void*)s, end - beg); + uptr d = (uptr)dst; + uptr beg = d & ~3UL; + // Copy left unaligned origin if that memory is poisoned. + if (beg < d) { + u32 o = get_origin_if_poisoned(beg, d - beg); + if (o) + *(u32 *)MEM_TO_ORIGIN(beg) = o; + beg += 4; + } + + uptr end = (d + size + 3) & ~3UL; + // Copy right unaligned origin if that memory is poisoned. + if (end > d + size) { + u32 o = get_origin_if_poisoned(d + size, end - d - size); + if (o) + *(u32 *)MEM_TO_ORIGIN(end - 4) = o; + end -= 4; + } + + if (beg < end) { + // Align src up. + uptr s = ((uptr)src + 3) & ~3UL; + fast_memcpy((void*)MEM_TO_ORIGIN(beg), (void*)MEM_TO_ORIGIN(s), end - beg); + } } void __msan_copy_poison(void *dst, const void *src, uptr size) { @@ -1119,6 +1435,9 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(mmap); INTERCEPT_FUNCTION(mmap64); INTERCEPT_FUNCTION(posix_memalign); + INTERCEPT_FUNCTION(memalign); + INTERCEPT_FUNCTION(valloc); + INTERCEPT_FUNCTION(pvalloc); INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); @@ -1126,15 +1445,18 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(fread); INTERCEPT_FUNCTION(fread_unlocked); INTERCEPT_FUNCTION(readlink); - INTERCEPT_FUNCTION(readdir); - INTERCEPT_FUNCTION(readdir64); INTERCEPT_FUNCTION(memcpy); + INTERCEPT_FUNCTION(memccpy); + INTERCEPT_FUNCTION(mempcpy); INTERCEPT_FUNCTION(memset); INTERCEPT_FUNCTION(memmove); + INTERCEPT_FUNCTION(bcopy); INTERCEPT_FUNCTION(wmemset); INTERCEPT_FUNCTION(wmemcpy); + INTERCEPT_FUNCTION(wmempcpy); INTERCEPT_FUNCTION(wmemmove); INTERCEPT_FUNCTION(strcpy); // NOLINT + INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); INTERCEPT_FUNCTION(__strdup); INTERCEPT_FUNCTION(strndup); @@ -1150,8 +1472,16 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(strtoul); INTERCEPT_FUNCTION(strtoull); INTERCEPT_FUNCTION(strtod); + INTERCEPT_FUNCTION(strtod_l); + INTERCEPT_FUNCTION(__strtod_l); INTERCEPT_FUNCTION(strtof); + INTERCEPT_FUNCTION(strtof_l); + INTERCEPT_FUNCTION(__strtof_l); INTERCEPT_FUNCTION(strtold); + INTERCEPT_FUNCTION(strtold_l); + INTERCEPT_FUNCTION(__strtold_l); + INTERCEPT_FUNCTION(vasprintf); + INTERCEPT_FUNCTION(asprintf); INTERCEPT_FUNCTION(vsprintf); INTERCEPT_FUNCTION(vsnprintf); INTERCEPT_FUNCTION(vswprintf); @@ -1159,20 +1489,24 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(snprintf); INTERCEPT_FUNCTION(swprintf); INTERCEPT_FUNCTION(strftime); - INTERCEPT_FUNCTION(wcstombs); - INTERCEPT_FUNCTION(mbstowcs); + INTERCEPT_FUNCTION(mbtowc); + INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(wcstod); INTERCEPT_FUNCTION(getenv); + INTERCEPT_FUNCTION(setenv); + INTERCEPT_FUNCTION(putenv); INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); INTERCEPT_FUNCTION(__fxstat); + INTERCEPT_FUNCTION(__fxstatat); INTERCEPT_FUNCTION(__xstat); INTERCEPT_FUNCTION(__lxstat); INTERCEPT_FUNCTION(__fxstat64); + INTERCEPT_FUNCTION(__fxstatat64); INTERCEPT_FUNCTION(__xstat64); INTERCEPT_FUNCTION(__lxstat64); INTERCEPT_FUNCTION(pipe); @@ -1180,28 +1514,33 @@ void InitializeInterceptors() { INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); INTERCEPT_FUNCTION(fgets_unlocked); - INTERCEPT_FUNCTION(getcwd); - INTERCEPT_FUNCTION(realpath); INTERCEPT_FUNCTION(getrlimit); INTERCEPT_FUNCTION(getrlimit64); - INTERCEPT_FUNCTION(statfs); - INTERCEPT_FUNCTION(fstatfs); - INTERCEPT_FUNCTION(statfs64); - INTERCEPT_FUNCTION(fstatfs64); INTERCEPT_FUNCTION(uname); INTERCEPT_FUNCTION(gethostname); INTERCEPT_FUNCTION(epoll_wait); INTERCEPT_FUNCTION(epoll_pwait); INTERCEPT_FUNCTION(recv); INTERCEPT_FUNCTION(recvfrom); - INTERCEPT_FUNCTION(recvmsg); INTERCEPT_FUNCTION(dladdr); + INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dlopen); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); INTERCEPT_FUNCTION(signal); INTERCEPT_FUNCTION(pthread_create); + INTERCEPT_FUNCTION(pthread_key_create); + INTERCEPT_FUNCTION(pthread_join); + INTERCEPT_FUNCTION(tzset); + INTERCEPT_FUNCTION(__cxa_atexit); + INTERCEPT_FUNCTION(shmat); + + if (REAL(pthread_key_create)(&g_thread_finalize_key, &thread_finalize)) { + Printf("MemorySanitizer: failed to create thread key\n"); + Die(); + } + inited = 1; } } // namespace __msan diff --git a/lib/msan/msan_interface_internal.h b/lib/msan/msan_interface_internal.h index fb57f67c42ed..794b3540f931 100644 --- a/lib/msan/msan_interface_internal.h +++ b/lib/msan/msan_interface_internal.h @@ -73,6 +73,8 @@ 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); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_alloca_origin4(void *a, uptr size, const char *descr, uptr pc); +SANITIZER_INTERFACE_ATTRIBUTE u32 __msan_get_origin(const void *a); SANITIZER_INTERFACE_ATTRIBUTE @@ -83,9 +85,12 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_set_exit_code(int exit_code); SANITIZER_INTERFACE_ATTRIBUTE +void __msan_set_keep_going(int keep_going); + +SANITIZER_INTERFACE_ATTRIBUTE int __msan_set_poison_in_malloc(int do_poison); -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE /* OPTIONAL */ const char* __msan_default_options(); // For testing. @@ -120,9 +125,51 @@ void __msan_partial_poison(const void* data, void* shadow, uptr size); // Memory will be marked uninitialized, with origin at the call site. SANITIZER_INTERFACE_ATTRIBUTE void __msan_allocated_memory(const void* data, uptr size); -} // extern "C" -// Unpoison first n function arguments. -void __msan_unpoison_param(uptr n); +SANITIZER_INTERFACE_ATTRIBUTE +u16 __sanitizer_unaligned_load16(const uu16 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u32 __sanitizer_unaligned_load32(const uu32 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +u64 __sanitizer_unaligned_load64(const uu64 *p); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store16(uu16 *p, u16 x); + +SANITIZER_INTERFACE_ATTRIBUTE +void __sanitizer_unaligned_store32(uu32 *p, u32 x); + +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); +} // extern "C" #endif // MSAN_INTERFACE_INTERNAL_H diff --git a/lib/msan/msan_linux.cc b/lib/msan/msan_linux.cc index 367dc904d05d..46f501e488c5 100644 --- a/lib/msan/msan_linux.cc +++ b/lib/msan/msan_linux.cc @@ -17,7 +17,6 @@ #include "msan.h" -#include <algorithm> #include <elf.h> #include <link.h> #include <stdio.h> @@ -46,7 +45,13 @@ static const uptr kOriginsBeg = kBad2Beg; static const uptr kOriginsEnd = kBad2End; bool InitShadow(bool prot1, bool prot2, bool map_shadow, bool init_origins) { - if (flags()->verbosity) { + if ((uptr) & InitShadow < kMemBeg) { + Printf("FATAL: Code below application range: %p < %p. Non-PIE build?\n", + &InitShadow, (void *)kMemBeg); + return false; + } + + 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); @@ -92,41 +97,6 @@ void InstallAtExitHandler() { atexit(MsanAtExit); } -void UnpoisonMappedDSO(link_map *map) { - typedef ElfW(Phdr) Elf_Phdr; - typedef ElfW(Ehdr) Elf_Ehdr; - char *base = (char *)map->l_addr; - Elf_Ehdr *ehdr = (Elf_Ehdr *)base; - char *phdrs = base + ehdr->e_phoff; - char *phdrs_end = phdrs + ehdr->e_phnum * ehdr->e_phentsize; - - // Find the segment with the minimum base so we can "relocate" the p_vaddr - // fields. Typically ET_DYN objects (DSOs) have base of zero and ET_EXEC - // objects have a non-zero base. - uptr preferred_base = ~0ULL; - for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { - Elf_Phdr *phdr = (Elf_Phdr *)iter; - if (phdr->p_type == PT_LOAD) - preferred_base = std::min(preferred_base, (uptr)phdr->p_vaddr); - } - - // Compute the delta from the real base to get a relocation delta. - sptr delta = (uptr)base - preferred_base; - // Now we can figure out what the loader really mapped. - for (char *iter = phdrs; iter != phdrs_end; iter += ehdr->e_phentsize) { - Elf_Phdr *phdr = (Elf_Phdr *)iter; - if (phdr->p_type == PT_LOAD) { - uptr seg_start = phdr->p_vaddr + delta; - uptr seg_end = seg_start + phdr->p_memsz; - // None of these values are aligned. We consider the ragged edges of the - // load command as defined, since they are mapped from the file. - seg_start = RoundDownTo(seg_start, GetPageSizeCached()); - seg_end = RoundUpTo(seg_end, GetPageSizeCached()); - __msan_unpoison((void *)seg_start, seg_end - seg_start); - } - } -} - } // namespace __msan #endif // __linux__ diff --git a/lib/msan/msan_new_delete.cc b/lib/msan/msan_new_delete.cc index 88d4364f6562..17687ddfc213 100644 --- a/lib/msan/msan_new_delete.cc +++ b/lib/msan/msan_new_delete.cc @@ -43,7 +43,8 @@ void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } #define OPERATOR_DELETE_BODY \ - if (ptr) MsanDeallocate(ptr) + 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; } diff --git a/lib/msan/msan_report.cc b/lib/msan/msan_report.cc index 734fc96fe69f..e3ef99307940 100644 --- a/lib/msan/msan_report.cc +++ b/lib/msan/msan_report.cc @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "msan.h" +#include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_mutex.h" @@ -24,16 +25,6 @@ using namespace __sanitizer; namespace __msan { -static bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} - class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -43,17 +34,12 @@ class Decorator: private __sanitizer::AnsiColorDecorator { const char *End() { return Default(); } }; -static void PrintStack(const uptr *trace, uptr size) { - SymbolizerScope sym_scope; - StackTrace::PrintStack(trace, size, true, - common_flags()->strip_path_prefix, 0); -} - static void DescribeOrigin(u32 origin) { Decorator d; - if (flags()->verbosity) + if (common_flags()->verbosity) Printf(" raw origin id: %d\n", origin); - if (const char *so = __msan_get_origin_descr_if_stack(origin)) { + uptr pc; + if (const char *so = GetOriginDescrIfStack(origin, &pc)) { char* s = internal_strdup(so); char* sep = internal_strchr(s, '@'); CHECK(sep); @@ -61,32 +47,25 @@ static void DescribeOrigin(u32 origin) { 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(), Demangle(sep + 1), - d.Origin(), d.End()); + 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); + } } else { uptr size = 0; const uptr *trace = StackDepotGet(origin, &size); Printf(" %sUninitialized value was created by a heap allocation%s\n", d.Origin(), d.End()); - PrintStack(trace, size); + StackTrace::PrintStack(trace, size); } } -static void ReportSummary(const char *error_type, StackTrace *stack) { - if (!stack->size || !IsSymbolizerAvailable()) return; - AddressInfo ai; - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - { - SymbolizerScope sym_scope; - SymbolizeCode(pc, &ai, 1); - } - ReportErrorSummary(error_type, - StripPathPrefix(ai.file, - common_flags()->strip_path_prefix), - ai.line, ai.function); -} - void ReportUMR(StackTrace *stack, u32 origin) { if (!__msan::flags()->report_umrs) return; @@ -94,20 +73,20 @@ void ReportUMR(StackTrace *stack, u32 origin) { Decorator d; Printf("%s", d.Warning()); - Report(" WARNING: Use of uninitialized value\n"); + Report(" WARNING: MemorySanitizer: use-of-uninitialized-value\n"); Printf("%s", d.End()); - PrintStack(stack->trace, stack->size); + StackTrace::PrintStack(stack->trace, stack->size); if (origin) { DescribeOrigin(origin); } - ReportSummary("use-of-uninitialized-value", stack); + ReportErrorSummary("use-of-uninitialized-value", stack); } void ReportExpectedUMRNotFound(StackTrace *stack) { SpinMutexLock l(&CommonSanitizerReportMutex); Printf(" WARNING: Expected use of uninitialized value not found\n"); - PrintStack(stack->trace, stack->size); + StackTrace::PrintStack(stack->trace, stack->size); } void ReportAtExitStatistics() { @@ -119,5 +98,4 @@ void ReportAtExitStatistics() { Printf("%s", d.End()); } - } // namespace __msan diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt index 7a784023d2db..9c49f167fa19 100644 --- a/lib/msan/tests/CMakeLists.txt +++ b/lib/msan/tests/CMakeLists.txt @@ -6,7 +6,6 @@ include_directories(..) include_directories(../..) # Instrumented libcxx sources and build flags. -set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx) file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp) set(MSAN_LIBCXX_CFLAGS -I${MSAN_LIBCXX_PATH}/include @@ -52,6 +51,7 @@ set(MSAN_UNITTEST_COMMON_CFLAGS -fno-exceptions -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer + -Wno-deprecated-declarations ) set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS ${MSAN_UNITTEST_COMMON_CFLAGS} @@ -165,7 +165,7 @@ macro(add_msan_tests_for_arch arch) ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO}) endmacro() -if(COMPILER_RT_CAN_EXECUTE_TESTS AND EXISTS ${MSAN_LIBCXX_PATH}/) +if(COMPILER_RT_CAN_EXECUTE_TESTS AND MSAN_CAN_INSTRUMENT_LIBCXX) if(CAN_TARGET_x86_64) add_msan_tests_for_arch(x86_64) endif() diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc index 1e05382e4b9f..f95bb4e7c618 100644 --- a/lib/msan/tests/msan_test.cc +++ b/lib/msan/tests/msan_test.cc @@ -19,12 +19,14 @@ #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> #include <arpa/inet.h> #include <dlfcn.h> @@ -33,11 +35,14 @@ #include <link.h> #include <limits.h> #include <sys/time.h> +#include <poll.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/resource.h> #include <sys/ioctl.h> +#include <sys/statvfs.h> +#include <sys/sysinfo.h> #include <sys/utsname.h> #include <sys/mman.h> #include <sys/vfs.h> @@ -45,6 +50,11 @@ #include <pwd.h> #include <sys/socket.h> #include <netdb.h> +#include <wordexp.h> +#include <mntent.h> +#include <netinet/ether.h> +#include <sys/ipc.h> +#include <sys/shm.h> #if defined(__i386__) || defined(__x86_64__) # include <emmintrin.h> @@ -53,6 +63,8 @@ # define MSAN_HAS_M128 0 #endif +static const int kPageSize = 4096; + typedef unsigned char U1; typedef unsigned short U2; // NOLINT typedef unsigned int U4; @@ -522,12 +534,42 @@ LargeStruct LargeRetTest() { return res; } +TEST(MemorySanitizer, strcmp) { + char s1[10]; + char s2[10]; + strncpy(s1, "foo", 10); + s2[0] = 'f'; + s2[1] = 'n'; + EXPECT_GT(strcmp(s1, s2), 0); + s2[1] = 'o'; + int res; + EXPECT_UMR(res = strcmp(s1, s2)); + EXPECT_NOT_POISONED(res); + EXPECT_EQ(strncmp(s1, s2, 1), 0); +} + TEST(MemorySanitizer, LargeRet) { LargeStruct a = LargeRetTest(); EXPECT_POISONED(a.x[0]); EXPECT_POISONED(a.x[9]); } +TEST(MemorySanitizer, strerror) { + char *buf = strerror(EINVAL); + EXPECT_NOT_POISONED(strlen(buf)); + buf = strerror(123456); + EXPECT_NOT_POISONED(strlen(buf)); +} + +TEST(MemorySanitizer, strerror_r) { + errno = 0; + char buf[1000]; + char *res = strerror_r(EINVAL, buf, sizeof(buf)); + ASSERT_EQ(0, errno); + if (!res) res = buf; // POSIX version success. + EXPECT_NOT_POISONED(strlen(res)); +} + TEST(MemorySanitizer, fread) { char *x = new char[32]; FILE *f = fopen("/proc/self/stat", "r"); @@ -566,6 +608,52 @@ TEST(MemorySanitizer, pread) { delete x; } +TEST(MemorySanitizer, readv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = readv(fd, iov, 2); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + +TEST(MemorySanitizer, preadv) { + char buf[2011]; + struct iovec iov[2]; + iov[0].iov_base = buf + 1; + iov[0].iov_len = 5; + iov[1].iov_base = buf + 10; + iov[1].iov_len = 2000; + int fd = open("/proc/self/stat", O_RDONLY); + assert(fd > 0); + int sz = preadv(fd, iov, 2, 3); + ASSERT_LT(sz, 5 + 2000); + ASSERT_GT(sz, iov[0].iov_len); + EXPECT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[1]); + EXPECT_NOT_POISONED(buf[5]); + EXPECT_POISONED(buf[6]); + EXPECT_POISONED(buf[9]); + EXPECT_NOT_POISONED(buf[10]); + EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); + EXPECT_POISONED(buf[11 + (sz - 1) - 5]); + close(fd); +} + // FIXME: fails now. TEST(MemorySanitizer, DISABLED_ioctl) { struct winsize ws; @@ -590,13 +678,47 @@ TEST(MemorySanitizer, stat) { EXPECT_NOT_POISONED(st->st_size); } +TEST(MemorySanitizer, fstatat) { + struct stat* st = new struct stat; + int dirfd = open("/proc/self", O_RDONLY); + assert(dirfd > 0); + int res = fstatat(dirfd, "stat", st, 0); + assert(!res); + EXPECT_NOT_POISONED(st->st_dev); + EXPECT_NOT_POISONED(st->st_mode); + EXPECT_NOT_POISONED(st->st_size); + close(dirfd); +} + TEST(MemorySanitizer, statfs) { - struct statfs* st = new struct statfs; - int res = statfs("/", st); + struct statfs st; + int res = statfs("/", &st); assert(!res); - EXPECT_NOT_POISONED(st->f_type); - EXPECT_NOT_POISONED(st->f_bfree); - EXPECT_NOT_POISONED(st->f_namelen); + EXPECT_NOT_POISONED(st.f_type); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namelen); +} + +TEST(MemorySanitizer, statvfs) { + struct statvfs st; + int res = statvfs("/", &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); +} + +TEST(MemorySanitizer, fstatvfs) { + struct statvfs st; + int fd = open("/", O_RDONLY | O_DIRECTORY); + int res = fstatvfs(fd, &st); + assert(!res); + EXPECT_NOT_POISONED(st.f_bsize); + EXPECT_NOT_POISONED(st.f_blocks); + EXPECT_NOT_POISONED(st.f_bfree); + EXPECT_NOT_POISONED(st.f_namemax); + close(fd); } TEST(MemorySanitizer, pipe) { @@ -629,6 +751,70 @@ TEST(MemorySanitizer, socketpair) { close(sv[1]); } +TEST(MemorySanitizer, poll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + res = poll(fds, 2, 500); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, ppoll) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + char data = 42; + res = write(pipefd[1], &data, 1); + ASSERT_EQ(1, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + fds[1].fd = pipefd[1]; + fds[1].events = POLLIN; + sigset_t ss; + sigemptyset(&ss); + res = ppoll(fds, 2, NULL, &ss); + ASSERT_EQ(1, res); + EXPECT_NOT_POISONED(fds[0].revents); + EXPECT_NOT_POISONED(fds[1].revents); + + close(pipefd[0]); + close(pipefd[1]); +} + +TEST(MemorySanitizer, poll_positive) { + int* pipefd = new int[2]; + int res = pipe(pipefd); + ASSERT_EQ(0, res); + + pollfd fds[2]; + fds[0].fd = pipefd[0]; + fds[0].events = POLLIN; + // fds[1].fd uninitialized + fds[1].events = POLLIN; + EXPECT_UMR(poll(fds, 2, 0)); + + close(pipefd[0]); + close(pipefd[1]); +} + TEST(MemorySanitizer, bind_getsockname) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); @@ -651,6 +837,83 @@ TEST(MemorySanitizer, bind_getsockname) { close(sock); } +TEST(MemorySanitizer, accept) { + int listen_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, listen_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(listen_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + res = listen(listen_socket, 1); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + int connect_socket = socket(AF_INET, SOCK_STREAM, 0); + ASSERT_LT(0, connect_socket); + res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); + ASSERT_EQ(0, res); + res = connect(connect_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(-1, res); + ASSERT_EQ(EINPROGRESS, errno); + + __msan_poison(&sai, sizeof(sai)); + int new_sock = accept(listen_socket, (struct sockaddr *)&sai, &sz); + ASSERT_LT(0, new_sock); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + __msan_poison(&sai, sizeof(sai)); + res = getpeername(new_sock, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + EXPECT_NOT_POISONED(sai); + + close(new_sock); + close(connect_socket); + close(listen_socket); +} + +TEST(MemorySanitizer, getaddrinfo) { + struct addrinfo *ai; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + int res = getaddrinfo("localhost", NULL, &hints, &ai); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(*ai); + ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); + EXPECT_NOT_POISONED(*(sockaddr_in*)ai->ai_addr); +} + +TEST(MemorySanitizer, getnameinfo) { + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 80; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + char host[500]; + char serv[500]; + int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, + sizeof(host), serv, sizeof(serv), 0); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(host[0]); + EXPECT_POISONED(host[sizeof(host) - 1]); + + ASSERT_NE(0, strlen(host)); + EXPECT_NOT_POISONED(serv[0]); + EXPECT_POISONED(serv[sizeof(serv) - 1]); + ASSERT_NE(0, strlen(serv)); +} + #define EXPECT_HOSTENT_NOT_POISONED(he) \ do { \ EXPECT_NOT_POISONED(*(he)); \ @@ -677,12 +940,87 @@ TEST(MemorySanitizer, gethostent) { EXPECT_HOSTENT_NOT_POISONED(he); } +#ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME + TEST(MemorySanitizer, gethostbyname) { struct hostent *he = gethostbyname("localhost"); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } +#endif // MSAN_TEST_DISABLE_GETHOSTBYNAME + +TEST(MemorySanitizer, recvmsg) { + int server_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, server_socket); + + struct sockaddr_in sai; + memset(&sai, 0, sizeof(sai)); + sai.sin_family = AF_INET; + sai.sin_port = 0; + sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + int res = bind(server_socket, (struct sockaddr *)&sai, sizeof(sai)); + ASSERT_EQ(0, res); + + socklen_t sz = sizeof(sai); + res = getsockname(server_socket, (struct sockaddr *)&sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(sai), sz); + + + int client_socket = socket(AF_INET, SOCK_DGRAM, 0); + ASSERT_LT(0, client_socket); + + struct sockaddr_in client_sai; + memset(&client_sai, 0, sizeof(client_sai)); + client_sai.sin_family = AF_INET; + client_sai.sin_port = 0; + client_sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + res = bind(client_socket, (struct sockaddr *)&client_sai, sizeof(client_sai)); + ASSERT_EQ(0, res); + + sz = sizeof(client_sai); + res = getsockname(client_socket, (struct sockaddr *)&client_sai, &sz); + ASSERT_EQ(0, res); + ASSERT_EQ(sizeof(client_sai), sz); + + + const char *s = "message text"; + struct iovec iov; + iov.iov_base = (void *)s; + iov.iov_len = strlen(s) + 1; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &sai; + msg.msg_namelen = sizeof(sai); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + res = sendmsg(client_socket, &msg, 0); + ASSERT_LT(0, res); + + + char buf[1000]; + struct iovec recv_iov; + recv_iov.iov_base = (void *)&buf; + recv_iov.iov_len = sizeof(buf); + struct sockaddr_in recv_sai; + struct msghdr recv_msg; + memset(&recv_msg, 0, sizeof(recv_msg)); + recv_msg.msg_name = &recv_sai; + recv_msg.msg_namelen = sizeof(recv_sai); + recv_msg.msg_iov = &recv_iov; + recv_msg.msg_iovlen = 1; + res = recvmsg(server_socket, &recv_msg, 0); + ASSERT_LT(0, res); + + ASSERT_EQ(sizeof(recv_sai), recv_msg.msg_namelen); + EXPECT_NOT_POISONED(*(struct sockaddr_in *)recv_msg.msg_name); + EXPECT_STREQ(s, buf); + + close(server_socket); + close(client_socket); +} + TEST(MemorySanitizer, gethostbyname2) { struct hostent *he = gethostbyname2("localhost", AF_INET); ASSERT_NE((void *)NULL, he); @@ -778,6 +1116,96 @@ TEST(MemorySanitizer, getcwd_gnu) { free(res); } +TEST(MemorySanitizer, get_current_dir_name) { + char* res = get_current_dir_name(); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, shmctl) { + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + struct shmid_ds ds; + int res = shmctl(id, IPC_STAT, &ds); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(ds); + + struct shminfo si; + res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(si); + + struct shm_info s_i; + res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); + ASSERT_GT(res, -1); + EXPECT_NOT_POISONED(s_i); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, shmat) { + void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + ASSERT_NE(MAP_FAILED, p); + + ((char *)p)[10] = *GetPoisoned<U1>(); + ((char *)p)[4095] = *GetPoisoned<U1>(); + + int res = munmap(p, 4096); + ASSERT_EQ(0, res); + + int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); + ASSERT_GT(id, -1); + + void *q = shmat(id, p, 0); + ASSERT_EQ(p, q); + + EXPECT_NOT_POISONED(((char *)q)[0]); + EXPECT_NOT_POISONED(((char *)q)[10]); + EXPECT_NOT_POISONED(((char *)q)[4095]); + + res = shmdt(q); + ASSERT_EQ(0, res); + + res = shmctl(id, IPC_RMID, 0); + ASSERT_GT(res, -1); +} + +TEST(MemorySanitizer, random_r) { + int32_t x; + char z[64]; + memset(z, 0, sizeof(z)); + + struct random_data buf; + memset(&buf, 0, sizeof(buf)); + + int res = initstate_r(0, z, sizeof(z), &buf); + ASSERT_EQ(0, res); + + res = random_r(&buf, &x); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(x); +} + +TEST(MemorySanitizer, confstr) { + char buf[3]; + size_t res = confstr(_CS_PATH, buf, sizeof(buf)); + ASSERT_GT(res, sizeof(buf)); + EXPECT_NOT_POISONED(buf[0]); + EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); + + char buf2[1000]; + res = confstr(_CS_PATH, buf2, sizeof(buf2)); + ASSERT_LT(res, sizeof(buf2)); + EXPECT_NOT_POISONED(buf2[0]); + EXPECT_NOT_POISONED(buf2[res - 1]); + EXPECT_POISONED(buf2[res]); + ASSERT_EQ(res, strlen(buf2) + 1); +} + TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); @@ -786,6 +1214,17 @@ TEST(MemorySanitizer, readdir) { closedir(dir); } +TEST(MemorySanitizer, readdir_r) { + DIR *dir = opendir("."); + struct dirent d; + struct dirent *pd; + int res = readdir_r(dir, &d, &pd); + assert(!res); + EXPECT_NOT_POISONED(pd); + EXPECT_NOT_POISONED(d.d_name[0]); + closedir(dir); +} + TEST(MemorySanitizer, realpath) { const char* relpath = "."; char path[PATH_MAX + 1]; @@ -794,6 +1233,42 @@ TEST(MemorySanitizer, realpath) { EXPECT_NOT_POISONED(path[0]); } +TEST(MemorySanitizer, realpath_null) { + const char* relpath = "."; + char* res = realpath(relpath, NULL); + printf("%d, %s\n", errno, strerror(errno)); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +TEST(MemorySanitizer, canonicalize_file_name) { + const char* relpath = "."; + char* res = canonicalize_file_name(relpath); + assert(res); + EXPECT_NOT_POISONED(res[0]); + free(res); +} + +extern char **environ; + +TEST(MemorySanitizer, setenv) { + setenv("AAA", "BBB", 1); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + +TEST(MemorySanitizer, putenv) { + char s[] = "AAA=BBB"; + putenv(s); + for (char **envp = environ; *envp; ++envp) { + EXPECT_NOT_POISONED(*envp); + EXPECT_NOT_POISONED(*envp[0]); + } +} + TEST(MemorySanitizer, memcpy) { char* x = new char[2]; char* y = new char[2]; @@ -804,6 +1279,35 @@ TEST(MemorySanitizer, memcpy) { EXPECT_POISONED(y[1]); } +void TestUnalignedMemcpy(int left, int right, bool src_is_aligned) { + const int sz = 20; + char *dst = (char *)malloc(sz); + U4 origin = __msan_get_origin(dst); + + char *src = (char *)malloc(sz); + 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]); + + 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); + } + } +} + TEST(MemorySanitizer, memmove) { char* x = new char[2]; char* y = new char[2]; @@ -814,6 +1318,63 @@ TEST(MemorySanitizer, memmove) { EXPECT_POISONED(y[1]); } +TEST(MemorySanitizer, memccpy_nomatch) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'd', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); + EXPECT_NOT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + memccpy(y, x, 'b', 4); + EXPECT_NOT_POISONED(y[0]); + EXPECT_NOT_POISONED(y[1]); + EXPECT_POISONED(y[2]); + EXPECT_POISONED(y[3]); + EXPECT_POISONED(y[4]); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_nomatch_positive) { + char* x = new char[5]; + char* y = new char[5]; + strcpy(x, "abc"); + EXPECT_UMR(memccpy(y, x, 'd', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, memccpy_match_positive) { + char* x = new char[5]; + char* y = new char[5]; + x[0] = 'a'; + x[2] = 'b'; + EXPECT_UMR(memccpy(y, x, 'b', 5)); + delete[] x; + delete[] y; +} + +TEST(MemorySanitizer, bcopy) { + char* x = new char[2]; + char* y = new char[2]; + x[0] = 1; + x[1] = *GetPoisoned<char>(); + bcopy(x, y, 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); +} + TEST(MemorySanitizer, strdup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); @@ -896,6 +1457,19 @@ TEST(MemorySanitizer, strncpy) { // NOLINT EXPECT_POISONED(y[2]); } +TEST(MemorySanitizer, stpcpy) { // NOLINT + char* x = new char[3]; + char* y = new char[3]; + x[0] = 'a'; + x[1] = *GetPoisoned<char>(1, 1); + x[2] = 0; + char *res = stpcpy(y, x); // NOLINT + ASSERT_EQ(res, y + 2); + EXPECT_NOT_POISONED(y[0]); + EXPECT_POISONED(y[1]); + EXPECT_NOT_POISONED(y[2]); +} + TEST(MemorySanitizer, strtol) { char *e; assert(1 == strtol("1", &e, 10)); @@ -920,12 +1494,35 @@ TEST(MemorySanitizer, strtoull) { EXPECT_NOT_POISONED((S8) e); } +TEST(MemorySanitizer, strtoimax) { + char *e; + assert(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)); EXPECT_NOT_POISONED((S8) e); } +#ifdef __GLIBC__ +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); +} +#endif // __GLIBC__ + TEST(MemorySanitizer, strtof) { char *e; assert(0 != strtof("1.5", &e)); @@ -938,6 +1535,121 @@ TEST(MemorySanitizer, strtold) { EXPECT_NOT_POISONED((S8) e); } +TEST(MemorySanitizer, modf) { + double x, y; + x = modf(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modff) { + float x, y; + x = modff(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, modfl) { + long double x, y; + x = modfl(2.1, &y); + EXPECT_NOT_POISONED(y); +} + +TEST(MemorySanitizer, sincos) { + double s, c; + sincos(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosf) { + float s, c; + sincosf(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, sincosl) { + long double s, c; + sincosl(0.2, &s, &c); + EXPECT_NOT_POISONED(s); + EXPECT_NOT_POISONED(c); +} + +TEST(MemorySanitizer, remquo) { + int quo; + double res = remquo(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquof) { + int quo; + float res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, remquol) { + int quo; + long double res = remquof(29.0, 3.0, &quo); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(quo); +} + +TEST(MemorySanitizer, lgamma) { + double res = lgamma(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammaf) { + float res = lgammaf(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgammal) { + long double res = lgammal(1.1); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(signgam); +} + +TEST(MemorySanitizer, lgamma_r) { + int sgn; + double res = lgamma_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammaf_r) { + int sgn; + float res = lgammaf_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, lgammal_r) { + int sgn; + long double res = lgammal_r(1.1, &sgn); + ASSERT_NE(0.0, res); + EXPECT_NOT_POISONED(sgn); +} + +TEST(MemorySanitizer, drand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + double d; + drand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + +TEST(MemorySanitizer, lrand48_r) { + struct drand48_data buf; + srand48_r(0, &buf); + long d; + lrand48_r(&buf, &d); + EXPECT_NOT_POISONED(d); +} + TEST(MemorySanitizer, sprintf) { // NOLINT char buff[10]; break_optimization(buff); @@ -981,6 +1693,33 @@ TEST(MemorySanitizer, swprintf) { EXPECT_POISONED(buff[8]); } +TEST(MemorySanitizer, asprintf) { // NOLINT + char *pbuf; + EXPECT_POISONED(pbuf); + int res = asprintf(&pbuf, "%d", 1234567); // NOLINT + assert(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); + free(pbuf); +} + +TEST(MemorySanitizer, mbstowcs) { + const char *x = "abc"; + wchar_t buff[10]; + int res = mbstowcs(buff, x, 2); + EXPECT_EQ(2, res); + EXPECT_EQ(L'a', buff[0]); + EXPECT_EQ(L'b', buff[1]); + EXPECT_POISONED(buff[2]); + res = mbstowcs(buff, x, 10); + EXPECT_EQ(3, res); + EXPECT_NOT_POISONED(buff[3]); +} + TEST(MemorySanitizer, wcstombs) { const wchar_t *x = L"abc"; char buff[10]; @@ -991,6 +1730,52 @@ TEST(MemorySanitizer, wcstombs) { EXPECT_EQ(buff[2], 'c'); } +TEST(MemorySanitizer, wcsrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsrtombs(buff, &p, 4, &mbs); + EXPECT_EQ(res, 3); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_EQ(buff[2], 'c'); + EXPECT_EQ(buff[3], '\0'); + EXPECT_POISONED(buff[4]); +} + +TEST(MemorySanitizer, wcsnrtombs) { + const wchar_t *x = L"abc"; + const wchar_t *p = x; + char buff[10]; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = wcsnrtombs(buff, &p, 2, 4, &mbs); + EXPECT_EQ(res, 2); + EXPECT_EQ(buff[0], 'a'); + EXPECT_EQ(buff[1], 'b'); + EXPECT_POISONED(buff[2]); +} + +TEST(MemorySanitizer, mbtowc) { + const char *x = "abc"; + wchar_t wx; + int res = mbtowc(&wx, x, 3); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + +TEST(MemorySanitizer, mbrtowc) { + const char *x = "abc"; + wchar_t wx; + mbstate_t mbs; + memset(&mbs, 0, sizeof(mbs)); + int res = mbrtowc(&wx, x, 3, &mbs); + EXPECT_GT(res, 0); + EXPECT_NOT_POISONED(wx); +} + TEST(MemorySanitizer, gettimeofday) { struct timeval tv; struct timezone tz; @@ -1060,6 +1845,13 @@ TEST(MemorySanitizer, getitimer) { assert(!res); } +TEST(MemorySanitizer, setitimer_null) { + setitimer(ITIMER_VIRTUAL, 0, 0); + // Not testing the return value, since it the behaviour seems to differ + // between libc implementations and POSIX. + // Should never crash, though. +} + TEST(MemorySanitizer, time) { time_t t; EXPECT_POISONED(t); @@ -1068,6 +1860,15 @@ TEST(MemorySanitizer, time) { 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); + EXPECT_NOT_POISONED(time.tm_sec); + EXPECT_NOT_POISONED(time.tm_hour); + EXPECT_NOT_POISONED(time.tm_year); +} + TEST(MemorySanitizer, localtime) { time_t t = 123; struct tm *time = localtime(&t); @@ -1076,6 +1877,7 @@ TEST(MemorySanitizer, localtime) { 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)); } TEST(MemorySanitizer, localtime_r) { @@ -1087,6 +1889,54 @@ TEST(MemorySanitizer, localtime_r) { 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)); +} + +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)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, getmntent_r) { + FILE *fp = setmntent("/etc/fstab", "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)); + EXPECT_NOT_POISONED(mnt->mnt_freq); + EXPECT_NOT_POISONED(mnt->mnt_passno); + fclose(fp); +} + +TEST(MemorySanitizer, ether) { + const char *asc = "11:22:33:44:55:66"; + struct ether_addr *paddr = ether_aton(asc); + EXPECT_NOT_POISONED(*paddr); + + struct ether_addr addr; + paddr = ether_aton_r(asc, &addr); + ASSERT_EQ(paddr, &addr); + EXPECT_NOT_POISONED(addr); + + char *s = ether_ntoa(&addr); + ASSERT_NE(0, strlen(s)); + + char buf[100]; + s = ether_ntoa_r(&addr, buf); + ASSERT_EQ(s, buf); + ASSERT_NE(0, strlen(buf)); } TEST(MemorySanitizer, mmap) { @@ -1201,6 +2051,39 @@ TEST(MemorySanitizer, sigaction) { } // namespace + +TEST(MemorySanitizer, sigemptyset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigemptyset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigfillset) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigfillset(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigpending) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigpending(&s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + +TEST(MemorySanitizer, sigprocmask) { + sigset_t s; + EXPECT_POISONED(s); + int res = sigprocmask(SIG_BLOCK, 0, &s); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(s); +} + struct StructWithDtor { ~StructWithDtor(); }; @@ -1392,22 +2275,33 @@ TEST(MemorySanitizer, VAArgOverflow) { static void vaargsfn_tlsoverwrite2(int guard, ...) { va_list vl; va_start(vl, guard); - EXPECT_NOT_POISONED(va_arg(vl, int)); + for (int i = 0; i < 20; ++i) + EXPECT_NOT_POISONED(va_arg(vl, int)); va_end(vl); } static void vaargsfn_tlsoverwrite(int guard, ...) { // This call will overwrite TLS contents unless it's backed up somewhere. - vaargsfn_tlsoverwrite2(2, 42); + vaargsfn_tlsoverwrite2(2, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42); // 20x va_list vl; va_start(vl, guard); - EXPECT_POISONED(va_arg(vl, int)); + for (int i = 0; i < 20; ++i) + EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTLSOverwrite) { int* x = GetPoisoned<int>(); - vaargsfn_tlsoverwrite(1, *x); + vaargsfn_tlsoverwrite(1, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x, + *x, *x, *x, *x, *x); // 20x + } struct StructByVal { @@ -1616,18 +2510,6 @@ extern char *program_invocation_name; # error "TODO: port this" #endif -// Compute the path to our loadable DSO. We assume it's in the same -// directory. Only use string routines that we intercept so far to do this. -static int PathToLoadable(char *buf, size_t sz) { - const char *basename = "libmsan_loadable.x86_64.so"; - char *argv0 = program_invocation_name; - char *last_slash = strrchr(argv0, '/'); - assert(last_slash); - int res = - snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); - return res < sz ? 0 : res; -} - static void dladdr_testfn() {} TEST(MemorySanitizer, dladdr) { @@ -1645,6 +2527,8 @@ TEST(MemorySanitizer, dladdr) { EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); } +#ifndef MSAN_TEST_DISABLE_DLOPEN + static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { (*(int *)data)++; EXPECT_NOT_POISONED(info->dlpi_addr); @@ -1655,6 +2539,18 @@ static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) return 0; } +// Compute the path to our loadable DSO. We assume it's in the same +// directory. Only use string routines that we intercept so far to do this. +static int PathToLoadable(char *buf, size_t sz) { + const char *basename = "libmsan_loadable.x86_64.so"; + char *argv0 = program_invocation_name; + char *last_slash = strrchr(argv0, '/'); + assert(last_slash); + int res = + snprintf(buf, sz, "%.*s/%s", int(last_slash - argv0), argv0, basename); + return res < sz ? 0 : res; +} + TEST(MemorySanitizer, dl_iterate_phdr) { char path[4096]; int res = PathToLoadable(path, sizeof(path)); @@ -1706,6 +2602,15 @@ TEST(MemorySanitizer, dlopenFailed) { ASSERT_EQ(0, lib); } +#endif // MSAN_TEST_DISABLE_DLOPEN + +TEST(MemorySanitizer, sched_getaffinity) { + cpu_set_t mask; + int res = sched_getaffinity(getpid(), sizeof(mask), &mask); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(mask); +} + TEST(MemorySanitizer, scanf) { const char *input = "42 hello"; int* d = new int; @@ -1737,8 +2642,7 @@ TEST(MemorySanitizer, SimpleThread) { EXPECT_NOT_POISONED(t); res = pthread_join(t, &p); assert(!res); - if (!__msan_has_dynamic_component()) // FIXME: intercept pthread_join (?). - __msan_unpoison(&p, sizeof(p)); + EXPECT_NOT_POISONED(p); delete (int*)p; } @@ -1783,6 +2687,71 @@ TEST(MemorySanitizer, PreAllocatedStackThread) { ASSERT_EQ(0, res); } +TEST(MemorySanitizer, pthread_attr_get) { + pthread_attr_t attr; + int res; + res = pthread_attr_init(&attr); + ASSERT_EQ(0, res); + { + int v; + res = pthread_attr_getdetachstate(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getguardsize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + struct sched_param v; + res = pthread_attr_getschedparam(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getschedpolicy(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getinheritsched(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + int v; + res = pthread_attr_getscope(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + size_t v; + res = pthread_attr_getstacksize(&attr, &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + { + void *v; + size_t w; + res = pthread_attr_getstack(&attr, &v, &w); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + EXPECT_NOT_POISONED(w); + } + { + cpu_set_t v; + res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); + ASSERT_EQ(0, res); + EXPECT_NOT_POISONED(v); + } + res = pthread_attr_destroy(&attr); + ASSERT_EQ(0, res); +} + TEST(MemorySanitizer, pthread_getschedparam) { int policy; struct sched_param param; @@ -1792,12 +2761,105 @@ TEST(MemorySanitizer, pthread_getschedparam) { EXPECT_NOT_POISONED(param.sched_priority); } +TEST(MemorySanitizer, pthread_key_create) { + pthread_key_t key; + int res = pthread_key_create(&key, NULL); + assert(!res); + EXPECT_NOT_POISONED(key); + res = pthread_key_delete(key); + assert(!res); +} + +namespace { +struct SignalCondArg { + pthread_cond_t* cond; + pthread_mutex_t* mu; + bool broadcast; +}; + +void *SignalCond(void *param) { + SignalCondArg *arg = reinterpret_cast<SignalCondArg *>(param); + pthread_mutex_lock(arg->mu); + if (arg->broadcast) + pthread_cond_broadcast(arg->cond); + else + pthread_cond_signal(arg->cond); + pthread_mutex_unlock(arg->mu); + return 0; +} +} // namespace + +TEST(MemorySanitizer, pthread_cond_wait) { + pthread_cond_t cond; + pthread_mutex_t mu; + SignalCondArg args = {&cond, &mu, false}; + pthread_cond_init(&cond, 0); + pthread_mutex_init(&mu, 0); + pthread_mutex_lock(&mu); + + // signal + pthread_t thr; + pthread_create(&thr, 0, SignalCond, &args); + int res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + // broadcast + args.broadcast = true; + pthread_create(&thr, 0, SignalCond, &args); + res = pthread_cond_wait(&cond, &mu); + assert(!res); + pthread_join(thr, 0); + + pthread_mutex_unlock(&mu); + pthread_mutex_destroy(&mu); + pthread_cond_destroy(&cond); +} + +TEST(MemorySanitizer, tmpnam) { + char s[L_tmpnam]; + char *res = tmpnam(s); + ASSERT_EQ(s, res); + EXPECT_NOT_POISONED(strlen(res)); +} + +TEST(MemorySanitizer, tempnam) { + char *res = tempnam(NULL, "zzz"); + EXPECT_NOT_POISONED(strlen(res)); + free(res); +} + TEST(MemorySanitizer, posix_memalign) { void *p; EXPECT_POISONED(p); int res = posix_memalign(&p, 4096, 13); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); + EXPECT_EQ(0U, (uintptr_t)p % 4096); + free(p); +} + +TEST(MemorySanitizer, memalign) { + void *p = memalign(4096, 13); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + free(p); +} + +TEST(MemorySanitizer, valloc) { + void *a = valloc(100); + EXPECT_EQ(0U, (uintptr_t)a % kPageSize); + free(a); +} + +TEST(MemorySanitizer, pvalloc) { + void *p = pvalloc(kPageSize + 100); + EXPECT_EQ(0U, (uintptr_t)p % kPageSize); + EXPECT_EQ(2 * kPageSize, __msan_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)); free(p); } @@ -1816,6 +2878,15 @@ TEST(MemorySanitizer, inet_pton) { EXPECT_NOT_POISONED(s_out[3]); } +TEST(MemorySanitizer, inet_aton) { + const char *s = "127.0.0.1"; + struct in_addr in[2]; + int res = inet_aton(s, in); + ASSERT_NE(0, res); + EXPECT_NOT_POISONED(in[0]); + EXPECT_POISONED(*(char *)(in + 1)); +} + TEST(MemorySanitizer, uname) { struct utsname u; int res = uname(&u); @@ -1834,6 +2905,13 @@ TEST(MemorySanitizer, gethostname) { EXPECT_NOT_POISONED(strlen(buf)); } +TEST(MemorySanitizer, sysinfo) { + struct sysinfo info; + int res = sysinfo(&info); + assert(!res); + EXPECT_NOT_POISONED(info); +} + TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root assert(p); @@ -1880,6 +2958,25 @@ TEST(MemorySanitizer, getgrnam_r) { EXPECT_NOT_POISONED(grp.gr_gid); } +TEST(MemorySanitizer, getgroups) { + int n = getgroups(0, 0); + gid_t *gids = new gid_t[n]; + int res = getgroups(n, gids); + ASSERT_EQ(n, res); + for (int i = 0; i < n; ++i) + EXPECT_NOT_POISONED(gids[i]); +} + +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_STREQ("a", w.we_wordv[0]); + ASSERT_STREQ("b", w.we_wordv[1]); + ASSERT_STREQ("c", w.we_wordv[2]); +} + template<class T> static bool applySlt(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); @@ -1986,6 +3083,83 @@ TEST(MemorySanitizer, VolatileBitfield) { EXPECT_POISONED((unsigned)S->y); } +TEST(MemorySanitizer, UnalignedLoad) { + char x[32]; + 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)); +} + +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]); + EXPECT_NOT_POISONED(x[2]); + EXPECT_POISONED(x[3]); + EXPECT_POISONED(x[4]); +} + +TEST(MemorySanitizer, UnalignedStore32) { + char x[8]; + U4 y4 = 0; + __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]); + EXPECT_NOT_POISONED(x[5]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_POISONED(x[7]); +} + +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]); + EXPECT_NOT_POISONED(x[6]); + EXPECT_NOT_POISONED(x[7]); + EXPECT_NOT_POISONED(x[8]); + EXPECT_POISONED(x[9]); + EXPECT_NOT_POISONED(x[10]); + EXPECT_POISONED(x[11]); +} + TEST(MemorySanitizerDr, StoreInDSOTest) { if (!__msan_has_dynamic_component()) return; char* s = new char[10]; @@ -2181,6 +3355,7 @@ void MemCpyTest() { T *x = new T[N]; T *y = new T[N]; T *z = new T[N]; + T *q = new T[N]; __msan_poison(x, N * sizeof(T)); __msan_set_origin(x, N * sizeof(T), ox); __msan_set_origin(y, N * sizeof(T), 777777); @@ -2191,6 +3366,12 @@ void MemCpyTest() { EXPECT_POISONED_O(y[N/2], ox); EXPECT_POISONED_O(y[N-1], ox); EXPECT_NOT_POISONED(x); + void *res = mempcpy(q, x, N * sizeof(T)); + ASSERT_EQ(q + N, res); + EXPECT_POISONED_O(q[0], ox); + EXPECT_POISONED_O(q[N/2], ox); + EXPECT_POISONED_O(q[N-1], ox); + EXPECT_NOT_POISONED(x); memmove(z, x, N * sizeof(T)); EXPECT_POISONED_O(z[0], ox); EXPECT_POISONED_O(z[N/2], ox); @@ -2325,14 +3506,56 @@ NOINLINE void RecursiveMalloc(int depth) { delete x2; } -TEST(MemorySanitizer, CallocOverflow) { - size_t kArraySize = 4096; - volatile size_t kMaxSizeT = std::numeric_limits<size_t>::max(); - volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - void *p = calloc(kArraySize, kArraySize2); // Should return 0. - EXPECT_EQ(0L, Ident(p)); +TEST(MemorySanitizer, Select) { + int x; + int volatile* p = &x; + int z = *p ? 1 : 0; + EXPECT_POISONED(z); } TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { RecursiveMalloc(22); } + +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]); + EXPECT_EQ(alloc_size, sizes[i]); + } +} + +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(__msan_get_ownership(int_ptr)); + EXPECT_EQ(sizeof(*int_ptr), __msan_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(__msan_get_ownership(array + 50)); + EXPECT_EQ(0, __msan_get_allocated_size(array + 50)); + + // 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)); + + delete int_ptr; +} + +TEST(MemorySanitizer, MlockTest) { + EXPECT_EQ(0, mlockall(MCL_CURRENT)); + EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); + EXPECT_EQ(0, munlockall()); + EXPECT_EQ(0, munlock((void*)0x987, 0x654)); +} |