diff options
author | Alexey Samsonov <samsonov@google.com> | 2014-02-14 13:02:58 +0000 |
---|---|---|
committer | Alexey Samsonov <samsonov@google.com> | 2014-02-14 13:02:58 +0000 |
commit | 5811f0e1e955866b76b0d9ebeccd2e4755c1dac8 (patch) | |
tree | 399a8d23e1b6d907d939583bf1fa2bd6fa1c5b79 /test/msan | |
parent | 3693bfe278d42da3891257b86c541e85fa9dec73 (diff) |
Move MSan lit-tests under test/msan
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@201412 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/msan')
83 files changed, 2257 insertions, 0 deletions
diff --git a/test/msan/CMakeLists.txt b/test/msan/CMakeLists.txt new file mode 100644 index 000000000..3387f8291 --- /dev/null +++ b/test/msan/CMakeLists.txt @@ -0,0 +1,22 @@ +set(MSAN_LIT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg) + +set(MSAN_TEST_DEPS + ${SANITIZER_COMMON_LIT_TEST_DEPS} + msan) + +if(LLVM_INCLUDE_TESTS AND COMPILER_RT_HAS_LIBCXX_SOURCES) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + list(APPEND MSAN_TEST_DEPS MsanUnitTests) +endif() + +add_lit_testsuite(check-msan "Running the MemorySanitizer tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS ${MSAN_TEST_DEPS} + ) +set_target_properties(check-msan PROPERTIES FOLDER "MSan tests") diff --git a/test/msan/Linux/getresid.cc b/test/msan/Linux/getresid.cc new file mode 100644 index 000000000..5021009e8 --- /dev/null +++ b/test/msan/Linux/getresid.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 + +#include <assert.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + uid_t uids[6]; + assert(0 == __msan_test_shadow(uids, 6 * sizeof(uid_t))); + assert(0 == getresuid(&uids[0], &uids[2], &uids[4])); + for (int i = 0; i < 3; i++) + assert(sizeof(uid_t) == + __msan_test_shadow(uids + 2 * i, 2 * sizeof(uid_t))); + + gid_t gids[6]; + assert(0 == __msan_test_shadow(gids, 6 * sizeof(gid_t))); + assert(0 == getresgid(&gids[0], &gids[2], &gids[4])); + for (int i = 0; i < 3; i++) + assert(sizeof(gid_t) == + __msan_test_shadow(gids + 2 * i, 2 * sizeof(gid_t))); + return 0; +} diff --git a/test/msan/Linux/glob.cc b/test/msan/Linux/glob.cc new file mode 100644 index 000000000..387ce3cf5 --- /dev/null +++ b/test/msan/Linux/glob.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 | FileCheck %s + +#include <assert.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +int main(int argc, char *argv[]) { + assert(argc == 2); + char buf[1024]; + snprintf(buf, sizeof(buf), "%s/%s", argv[1], "glob_test_root/*a"); + + glob_t globbuf; + int res = glob(buf, 0, 0, &globbuf); + + printf("%d %s\n", errno, strerror(errno)); + assert(res == 0); + assert(globbuf.gl_pathc == 2); + printf("%zu\n", strlen(globbuf.gl_pathv[0])); + printf("%zu\n", strlen(globbuf.gl_pathv[1])); + printf("PASS\n"); + // CHECK: PASS + return 0; +} diff --git a/test/msan/Linux/glob_altdirfunc.cc b/test/msan/Linux/glob_altdirfunc.cc new file mode 100644 index 000000000..b8200c3ee --- /dev/null +++ b/test/msan/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/test/msan/Linux/glob_nomatch.cc b/test/msan/Linux/glob_nomatch.cc new file mode 100644 index 000000000..0262034ae --- /dev/null +++ b/test/msan/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/test/msan/Linux/glob_test_root/aa b/test/msan/Linux/glob_test_root/aa new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/Linux/glob_test_root/aa diff --git a/test/msan/Linux/glob_test_root/ab b/test/msan/Linux/glob_test_root/ab new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/Linux/glob_test_root/ab diff --git a/test/msan/Linux/glob_test_root/ba b/test/msan/Linux/glob_test_root/ba new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/Linux/glob_test_root/ba diff --git a/test/msan/Linux/lit.local.cfg b/test/msan/Linux/lit.local.cfg new file mode 100644 index 000000000..57271b807 --- /dev/null +++ b/test/msan/Linux/lit.local.cfg @@ -0,0 +1,9 @@ +def getRoot(config): + if not config.parent: + return config + return getRoot(config.parent) + +root = getRoot(config) + +if root.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/msan/Linux/syscalls.cc b/test/msan/Linux/syscalls.cc new file mode 100644 index 000000000..ab5be3802 --- /dev/null +++ b/test/msan/Linux/syscalls.cc @@ -0,0 +1,107 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t 2>&1 + +#include <assert.h> +#include <errno.h> +#include <glob.h> +#include <stdio.h> +#include <string.h> + +#include <linux/aio_abi.h> +#include <sys/ptrace.h> +#include <sys/stat.h> + +#include <sanitizer/linux_syscall_hooks.h> +#include <sanitizer/msan_interface.h> + +/* Test the presence of __sanitizer_syscall_ in the tool runtime, and general + sanity of their behaviour. */ + +int main(int argc, char *argv[]) { + char buf[1000]; + const int kTen = 10; + const int kFortyTwo = 42; + memset(buf, 0, sizeof(buf)); + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_pre_recvmsg(0, buf, 0); + __sanitizer_syscall_pre_rt_sigpending(buf, kTen); + __sanitizer_syscall_pre_getdents(0, buf, kTen); + __sanitizer_syscall_pre_getdents64(0, buf, kTen); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(0, 0, buf, 0); + __sanitizer_syscall_post_rt_sigpending(-1, buf, kTen); + __sanitizer_syscall_post_getdents(0, 0, buf, kTen); + __sanitizer_syscall_post_getdents64(0, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == -1); + + __msan_unpoison(buf, sizeof(buf)); + __sanitizer_syscall_post_recvmsg(kTen, 0, buf, 0); + + // Tell the kernel that the output struct size is 10 bytes, verify that those + // bytes are unpoisoned, and the next byte is not. + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_rt_sigpending(0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, kTen + 1); + __sanitizer_syscall_post_getdents64(kTen, 0, buf, kTen); + assert(__msan_test_shadow(buf, sizeof(buf)) == kTen); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_getres(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(0, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(long) * 2); + + // Failed syscall does not write to the buffer. + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_clock_gettime(-1, 0, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == 0); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_read(5, 42, buf, 10); + assert(__msan_test_shadow(buf, sizeof(buf)) == 5); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_newfstatat(0, 5, "/path/to/file", buf, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(struct stat)); + + __msan_poison(buf, sizeof(buf)); + int prio = 0; + __sanitizer_syscall_post_mq_timedreceive(kFortyTwo, 5, buf, sizeof(buf), &prio, 0); + assert(__msan_test_shadow(buf, sizeof(buf)) == kFortyTwo); + assert(__msan_test_shadow(&prio, sizeof(prio)) == -1); + + __msan_poison(buf, sizeof(buf)); + __sanitizer_syscall_post_ptrace(0, PTRACE_PEEKUSER, kFortyTwo, 0xABCD, buf); + assert(__msan_test_shadow(buf, sizeof(buf)) == sizeof(void *)); + + __msan_poison(buf, sizeof(buf)); + struct iocb iocb[2]; + struct iocb *iocbp[2] = { &iocb[0], &iocb[1] }; + memset(iocb, 0, sizeof(iocb)); + iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[0].aio_buf = (__u64)buf; + iocb[0].aio_nbytes = kFortyTwo; + iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[1].aio_buf = (__u64)(&buf[kFortyTwo]); + iocb[1].aio_nbytes = kFortyTwo; + __sanitizer_syscall_pre_io_submit(0, 2, &iocbp); + assert(__msan_test_shadow(buf, sizeof(buf)) == 2 * kFortyTwo); + + __msan_poison(buf, sizeof(buf)); + char *p = buf; + __msan_poison(&p, sizeof(p)); + __sanitizer_syscall_post_io_setup(0, 1, &p); + assert(__msan_test_shadow(&p, sizeof(p)) == -1); + assert(__msan_test_shadow(buf, sizeof(buf)) >= 32); + + return 0; +} diff --git a/test/msan/Linux/tcgetattr.cc b/test/msan/Linux/tcgetattr.cc new file mode 100644 index 000000000..e6e101db8 --- /dev/null +++ b/test/msan/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/test/msan/Linux/xattr.cc b/test/msan/Linux/xattr.cc new file mode 100644 index 000000000..583b2e3a9 --- /dev/null +++ b/test/msan/Linux/xattr.cc @@ -0,0 +1,142 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 + +#include <argz.h> +#include <assert.h> +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sanitizer/msan_interface.h> + +// Do not depend on libattr headers. +#ifndef ENOATTR +#define ENOATTR ENODATA +#endif + +extern "C" { +ssize_t listxattr(const char *path, char *list, size_t size); +ssize_t llistxattr(const char *path, char *list, size_t size); +ssize_t flistxattr(int fd, char *list, size_t size); +ssize_t getxattr(const char *path, const char *name, void *value, size_t size); +ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size); +ssize_t fgetxattr(int fd, const char *name, void *value, size_t size); +} + +char g_path[1024]; +int g_fd; + +// Life before closures... +ssize_t listxattr_wrapper(char *buf, size_t size) { + return listxattr(g_path, buf, size); +} + +ssize_t llistxattr_wrapper(char *buf, size_t size) { + return llistxattr(g_path, buf, size); +} + +ssize_t flistxattr_wrapper(char *buf, size_t size) { + return flistxattr(g_fd, buf, size); +} + +ssize_t getxattr_wrapper(const char *name, char *buf, size_t size) { + return getxattr(g_path, name, buf, size); +} + +ssize_t lgetxattr_wrapper(const char *name, char *buf, size_t size) { + return lgetxattr(g_path, name, buf, size); +} + +ssize_t fgetxattr_wrapper(const char *name, char *buf, size_t size) { + return fgetxattr(g_fd, name, buf, size); +} + +size_t test_list(ssize_t fun(char*, size_t), char **buf) { + int buf_size = 1024; + while (true) { + *buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(*buf, buf_size) != -1); + ssize_t res = fun(*buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(*buf, buf_size) == res); + return res; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + assert(errno == ERANGE); + free(*buf); + buf_size *= 2; + } +} + +// True means success. False means result inconclusive because we don't have +// access to this attribute. +bool test_get_single_attr(ssize_t fun(const char *, char *, size_t), + const char *attr_name) { + char *buf; + int buf_size = 1024; + while (true) { + buf = (char *)malloc(buf_size); + assert(__msan_test_shadow(buf, buf_size) != -1); + ssize_t res = fun(attr_name, buf, buf_size); + if (res >= 0) { + assert(__msan_test_shadow(buf, buf_size) == res); + free(buf); + return true; + } + if (errno == ENOTSUP) { + printf("Extended attributes are disabled. *xattr test is a no-op.\n"); + exit(0); + } + if (errno == ENOATTR) + return false; + assert(errno == ERANGE); + free(buf); + buf_size *= 2; + } +} + +void test_get(ssize_t fun(const char *, char *, size_t), const char *attr_list, + size_t attr_list_size) { + // Try every attribute, until we see one we can access. Attribute names are + // null-separated strings in attr_list. + size_t attr_list_len = argz_count(attr_list, attr_list_size); + char **attrs = (char **)malloc((attr_list_len + 1) * sizeof(char *)); + size_t i; + for (i = 0; (i < attr_list_len) && attrs[i]; i++) { + if (test_get_single_attr(fun, attrs[i])) + return; + } + printf("*xattr test could not access any attributes.\n"); +} + +// TODO: set some attributes before trying to retrieve them with *getxattr. +// Currently the list is empty, so *getxattr is not tested. +int main(int argc, char *argv[]) { + assert(argc == 2); + snprintf(g_path, sizeof(g_path), "%s/%s", argv[1], "xattr_test_root/a"); + + g_fd = open(g_path, O_RDONLY); + assert(g_fd); + + char *attr_list; + size_t attr_list_size; + attr_list_size = test_list(listxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(llistxattr_wrapper, &attr_list); + free(attr_list); + attr_list_size = test_list(flistxattr_wrapper, &attr_list); + + test_get(getxattr_wrapper, attr_list, attr_list_size); + test_get(lgetxattr_wrapper, attr_list, attr_list_size); + test_get(fgetxattr_wrapper, attr_list, attr_list_size); + + free(attr_list); + return 0; +} diff --git a/test/msan/Linux/xattr_test_root/a b/test/msan/Linux/xattr_test_root/a new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/Linux/xattr_test_root/a diff --git a/test/msan/SharedLibs/dso-origin-so.cc b/test/msan/SharedLibs/dso-origin-so.cc new file mode 100644 index 000000000..8930a7159 --- /dev/null +++ b/test/msan/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/test/msan/SharedLibs/dso-origin.h b/test/msan/SharedLibs/dso-origin.h new file mode 100644 index 000000000..ff926b3f6 --- /dev/null +++ b/test/msan/SharedLibs/dso-origin.h @@ -0,0 +1,4 @@ +extern "C" { +void my_access(int *p); +void *my_alloc(unsigned sz); +} diff --git a/test/msan/SharedLibs/lit.local.cfg b/test/msan/SharedLibs/lit.local.cfg new file mode 100644 index 000000000..b3677c17a --- /dev/null +++ b/test/msan/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/test/msan/Unit/lit.site.cfg.in b/test/msan/Unit/lit.site.cfg.in new file mode 100644 index 000000000..dc0e9613d --- /dev/null +++ b/test/msan/Unit/lit.site.cfg.in @@ -0,0 +1,14 @@ +## Autogenerated by LLVM/Clang configuration. +# Do not edit! + +# Load common config for all compiler-rt unit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/unittests/lit.common.unit.configured") + +# Setup config name. +config.name = 'MemorySanitizer-Unit' + +# Setup test source and exec root. For unit tests, we define +# it as build directory with MSan unit tests. +# FIXME: Don't use hardcoded path to MSan unit tests. +config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/msan/tests" +config.test_source_root = config.test_exec_root diff --git a/test/msan/allocator_returns_null.cc b/test/msan/allocator_returns_null.cc new file mode 100644 index 000000000..aaa85cce7 --- /dev/null +++ b/test/msan/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/test/msan/backtrace.cc b/test/msan/backtrace.cc new file mode 100644 index 000000000..48684c29c --- /dev/null +++ b/test/msan/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/test/msan/c-strdup.c b/test/msan/c-strdup.c new file mode 100644 index 000000000..7772f0f30 --- /dev/null +++ b/test/msan/c-strdup.c @@ -0,0 +1,17 @@ +// RUN: %clang_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 + +// Test that strdup in C programs is intercepted. +// GLibC headers translate strdup to __strdup at -O1 and higher. + +#include <stdlib.h> +#include <string.h> +int main(int argc, char **argv) { + char buf[] = "abc"; + char *p = strdup(buf); + if (*p) + exit(0); + return 0; +} diff --git a/test/msan/cxa_atexit.cc b/test/msan/cxa_atexit.cc new file mode 100644 index 000000000..f3641aadc --- /dev/null +++ b/test/msan/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/test/msan/default_blacklist.cc b/test/msan/default_blacklist.cc new file mode 100644 index 000000000..32cc02257 --- /dev/null +++ b/test/msan/default_blacklist.cc @@ -0,0 +1,3 @@ +// Test that MSan uses the default blacklist from resource directory. +// RUN: %clangxx_msan -### %s 2>&1 | FileCheck %s +// CHECK: fsanitize-blacklist={{.*}}msan_blacklist.txt diff --git a/test/msan/dlerror.cc b/test/msan/dlerror.cc new file mode 100644 index 000000000..281b3164f --- /dev/null +++ b/test/msan/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/test/msan/dso-origin.cc b/test/msan/dso-origin.cc new file mode 100644 index 000000000..13661c65e --- /dev/null +++ b/test/msan/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/test/msan/dtls_test.c b/test/msan/dtls_test.c new file mode 100644 index 000000000..c88e50f1d --- /dev/null +++ b/test/msan/dtls_test.c @@ -0,0 +1,60 @@ +/* RUN: %clang_msan -m64 %s -o %t + RUN: %clang_msan -m64 %s -DBUILD_SO -fPIC -o %t-so.so -shared + RUN: not %t 2>&1 | FileCheck %s + CHECK: MemorySanitizer: use-of-uninitialized-value + + This is an actual bug in msan/glibc integration, + see https://sourceware.org/bugzilla/show_bug.cgi?id=16291 +*/ + +#ifndef BUILD_SO +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> + +typedef long *(* get_t)(); +get_t GetTls; +void *Thread1(void *unused) { + long uninitialized; + long *x = GetTls(); + if (*x) + fprintf(stderr, "bar\n"); + *x = uninitialized; + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + return 0; +} + +void *Thread2(void *unused) { + long *x = GetTls(); + fprintf(stderr, "stack: %p dtls: %p\n", &x, x); + if (*x) + fprintf(stderr, "foo\n"); // False negative here. + return 0; +} + +int main(int argc, char *argv[]) { + char path[4096]; + snprintf(path, sizeof(path), "%s-so.so", argv[0]); + int i; + + void *handle = dlopen(path, RTLD_LAZY); + if (!handle) fprintf(stderr, "%s\n", dlerror()); + assert(handle != 0); + GetTls = (get_t)dlsym(handle, "GetTls"); + assert(dlerror() == 0); + + pthread_t t; + pthread_create(&t, 0, Thread1, 0); + pthread_join(t, 0); + pthread_create(&t, 0, Thread2, 0); + pthread_join(t, 0); + return 0; +} +#else // BUILD_SO +__thread long huge_thread_local_array[1 << 17]; +long *GetTls() { + return &huge_thread_local_array[0]; +} +#endif diff --git a/test/msan/errno.cc b/test/msan/errno.cc new file mode 100644 index 000000000..af27ad0b0 --- /dev/null +++ b/test/msan/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/test/msan/getaddrinfo-positive.cc b/test/msan/getaddrinfo-positive.cc new file mode 100644 index 000000000..7fde1fdfa --- /dev/null +++ b/test/msan/getaddrinfo-positive.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdlib.h> + +volatile int z; + +int main(void) { + struct addrinfo *ai; + struct addrinfo hint; + int res = getaddrinfo("localhost", NULL, NULL, &ai); + if (ai) z = 1; // OK + res = getaddrinfo("localhost", NULL, &hint, &ai); + // CHECK: UMR in __interceptor_getaddrinfo at offset 0 inside + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main {{.*}}getaddrinfo-positive.cc:[[@LINE-3]] + return 0; +} diff --git a/test/msan/getaddrinfo.cc b/test/msan/getaddrinfo.cc new file mode 100644 index 000000000..0518cf473 --- /dev/null +++ b/test/msan/getaddrinfo.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#include <stdlib.h> + +void poison_stack_ahead() { + char buf[100000]; + // With -O0 this poisons a large chunk of stack. +} + +int main(void) { + poison_stack_ahead(); + + struct addrinfo *ai; + + // This should trigger loading of libnss_dns and friends. + // Those libraries are typically uninstrumented.They will call strlen() on a + // stack-allocated buffer, which is very likely to be poisoned. Test that we + // don't report this as an UMR. + int res = getaddrinfo("not-in-etc-hosts", NULL, NULL, &ai); + return 0; +} diff --git a/test/msan/getline.cc b/test/msan/getline.cc new file mode 100644 index 000000000..27168a885 --- /dev/null +++ b/test/msan/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/test/msan/getline_test_data b/test/msan/getline_test_data new file mode 100644 index 000000000..5ba1d4cec --- /dev/null +++ b/test/msan/getline_test_data @@ -0,0 +1,2 @@ +abcde +12345 diff --git a/test/msan/heap-origin.cc b/test/msan/heap-origin.cc new file mode 100644 index 000000000..dfe7edd27 --- /dev/null +++ b/test/msan/heap-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + char *volatile x = (char*)malloc(5 * sizeof(char)); + return *x; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*heap-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by a heap allocation + // CHECK-ORIGINS: {{#0 0x.* in .*malloc}} + // CHECK-ORIGINS: {{#1 0x.* in main .*heap-origin.cc:}}[[@LINE-7]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*heap-origin.cc:.* main}} +} diff --git a/test/msan/iconv.cc b/test/msan/iconv.cc new file mode 100644 index 000000000..e7c30edd9 --- /dev/null +++ b/test/msan/iconv.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %t |& FileCheck %s + +#include <assert.h> +#include <iconv.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +int main(void) { + iconv_t cd = iconv_open("ASCII", "ASCII"); + assert(cd != (iconv_t)-1); + + char inbuf_[100]; + strcpy(inbuf_, "sample text"); + char outbuf_[100]; + char *inbuf = inbuf_; + char *outbuf = outbuf_; + size_t inbytesleft = strlen(inbuf_); + size_t outbytesleft = sizeof(outbuf_); + +#ifdef POSITIVE + { + char u; + char *volatile p = &u; + inbuf_[5] = *p; + } +#endif + + size_t res; + res = iconv(cd, 0, 0, 0, 0); + assert(res != (size_t)-1); + + res = iconv(cd, 0, 0, &outbuf, &outbytesleft); + assert(res != (size_t)-1); + + res = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 {{.*}} in main {{.*}}iconv.cc:[[@LINE-2]] + assert(res != (size_t)-1); + assert(inbytesleft == 0); + + assert(memcmp(inbuf_, outbuf_, strlen(inbuf_)) == 0); + + iconv_close(cd); + return 0; +} diff --git a/test/msan/if_indextoname.cc b/test/msan/if_indextoname.cc new file mode 100644 index 000000000..7a5ba3599 --- /dev/null +++ b/test/msan/if_indextoname.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t 2>&1 + +#include <assert.h> +#include <errno.h> +#include <net/if.h> +#include <stdio.h> +#include <string.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + char ifname[IF_NAMESIZE + 1]; + assert(0 == __msan_test_shadow(ifname, sizeof(ifname))); + if (!if_indextoname(1, ifname)) { + assert(errno == ENXIO); + printf("No network interfaces found.\n"); + return 0; + } + assert(strlen(ifname) + 1 == __msan_test_shadow(ifname, sizeof(ifname))); + return 0; +} diff --git a/test/msan/ifaddrs.cc b/test/msan/ifaddrs.cc new file mode 100644 index 000000000..fe11a9542 --- /dev/null +++ b/test/msan/ifaddrs.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t %p 2>&1 + +#include <assert.h> +#include <errno.h> +#include <ifaddrs.h> +#include <stdio.h> +#include <string.h> + +#include <vector> + +#include <sanitizer/msan_interface.h> + +#define CHECK_AND_PUSH(addr, size) \ + if (addr) { \ + assert(-1 == __msan_test_shadow(addr, sizeof(size))); \ + ranges.push_back(std::make_pair((void *)addr, (size_t)size)); \ + } + +int main(int argc, char *argv[]) { + struct ifaddrs *ifas; + + assert(0 == __msan_test_shadow(&ifas, sizeof(ifaddrs *))); + int res = getifaddrs(&ifas); + if (res == -1) { + assert(errno == ENOSYS); + printf("getifaddrs() is not implemented\n"); + return 0; + } + assert(res == 0); + assert(-1 == __msan_test_shadow(&ifas, sizeof(ifaddrs *))); + + std::vector<std::pair<void *, size_t> > ranges; + ifaddrs *p = ifas; + while (p) { + CHECK_AND_PUSH(p, sizeof(ifaddrs)); + CHECK_AND_PUSH(p->ifa_name, strlen(p->ifa_name) + 1); + CHECK_AND_PUSH(p->ifa_addr, sizeof(*p->ifa_addr)); + CHECK_AND_PUSH(p->ifa_netmask, sizeof(*p->ifa_netmask)); + CHECK_AND_PUSH(p->ifa_broadaddr, sizeof(*p->ifa_broadaddr)); + CHECK_AND_PUSH(p->ifa_dstaddr, sizeof(*p->ifa_dstaddr)); + p = p->ifa_next; + } + + freeifaddrs(ifas); + for (int i = 0; i < ranges.size(); i++) + assert(0 == __msan_test_shadow(ranges[i].first, ranges[i].second)); + return 0; +} diff --git a/test/msan/initgroups.cc b/test/msan/initgroups.cc new file mode 100644 index 000000000..adba53695 --- /dev/null +++ b/test/msan/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/test/msan/inline.cc b/test/msan/inline.cc new file mode 100644 index 000000000..4aeb15583 --- /dev/null +++ b/test/msan/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/test/msan/insertvalue_origin.cc b/test/msan/insertvalue_origin.cc new file mode 100644 index 000000000..769ea45f8 --- /dev/null +++ b/test/msan/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/test/msan/ioctl.cc b/test/msan/ioctl.cc new file mode 100644 index 000000000..caff80c2e --- /dev/null +++ b/test/msan/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/test/msan/ioctl_custom.cc b/test/msan/ioctl_custom.cc new file mode 100644 index 000000000..94ed528c7 --- /dev/null +++ b/test/msan/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/test/msan/ioctl_sound.cc b/test/msan/ioctl_sound.cc new file mode 100644 index 000000000..0611e0dc4 --- /dev/null +++ b/test/msan/ioctl_sound.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %t + +#include <assert.h> +#include <fcntl.h> +#include <sound/asound.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + int fd = open("/dev/snd/controlC0", O_RDONLY); + if (fd < 0) { + printf("Unable to open sound device."); + return 0; + } + const unsigned sz = sizeof(snd_ctl_card_info); + void *info = malloc(sz + 1); + assert(__msan_test_shadow(info, sz) == 0); + assert(ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, info) >= 0); + assert(__msan_test_shadow(info, sz + 1) == sz); + close(fd); + free(info); + return 0; +} diff --git a/test/msan/keep-going-dso.cc b/test/msan/keep-going-dso.cc new file mode 100644 index 000000000..6d006756a --- /dev/null +++ b/test/msan/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/test/msan/keep-going.cc b/test/msan/keep-going.cc new file mode 100644 index 000000000..e33b137c7 --- /dev/null +++ b/test/msan/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/test/msan/lit.cfg b/test/msan/lit.cfg new file mode 100644 index 000000000..3031e6aed --- /dev/null +++ b/test/msan/lit.cfg @@ -0,0 +1,31 @@ +# -*- Python -*- + +import os + +# Setup config name. +config.name = 'MemorySanitizer' + +# Setup source root. +config.test_source_root = os.path.dirname(__file__) + +# Setup default compiler flags used with -fsanitize=memory option. +clang_msan_cflags = ["-fsanitize=memory", + "-mno-omit-leaf-frame-pointer", + "-fno-omit-frame-pointer", + "-fno-optimize-sibling-calls", + "-g", + "-m64"] +clang_msan_cxxflags = ["--driver-mode=g++ "] + clang_msan_cflags +config.substitutions.append( ("%clang_msan ", + " ".join([config.clang] + clang_msan_cflags) + + " ") ) +config.substitutions.append( ("%clangxx_msan ", + " ".join([config.clang] + clang_msan_cxxflags) + + " ") ) + +# Default test suffixes. +config.suffixes = ['.c', '.cc', '.cpp'] + +# MemorySanitizer tests are currently supported on Linux only. +if config.host_os not in ['Linux']: + config.unsupported = True diff --git a/test/msan/lit.site.cfg.in b/test/msan/lit.site.cfg.in new file mode 100644 index 000000000..fb22a57a9 --- /dev/null +++ b/test/msan/lit.site.cfg.in @@ -0,0 +1,5 @@ +# Load common config for all compiler-rt lit tests. +lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") + +# Load tool-specific config that would do the real work. +lit_config.load_config(config, "@MSAN_LIT_SOURCE_DIR@/lit.cfg") diff --git a/test/msan/malloc_hook.cc b/test/msan/malloc_hook.cc new file mode 100644 index 000000000..fc68fbc35 --- /dev/null +++ b/test/msan/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/test/msan/mmap_below_shadow.cc b/test/msan/mmap_below_shadow.cc new file mode 100644 index 000000000..eb8b0e981 --- /dev/null +++ b/test/msan/mmap_below_shadow.cc @@ -0,0 +1,28 @@ +// Test mmap behavior when map address is below shadow range. +// With MAP_FIXED, we return EINVAL. +// Without MAP_FIXED, we ignore the address hint and map somewhere in +// application range. + +// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 -D_FILE_OFFSET_BITS=64 %s -o %t && %t + +#include <assert.h> +#include <errno.h> +#include <stdint.h> +#include <sys/mman.h> + +int main(void) { + // Hint address just below shadow. + uintptr_t hint = 0x4f0000000000ULL; + const uintptr_t app_start = 0x600000000000ULL; + uintptr_t p = (uintptr_t)mmap( + (void *)hint, 4096, PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | (FIXED ? MAP_FIXED : 0), -1, 0); + if (FIXED) + assert(p == (uintptr_t)-1 && errno == EINVAL); + else + assert(p >= app_start); + return 0; +} diff --git a/test/msan/no_sanitize_memory.cc b/test/msan/no_sanitize_memory.cc new file mode 100644 index 000000000..48afc17e3 --- /dev/null +++ b/test/msan/no_sanitize_memory.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t >%t.out 2>&1 + +// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %t >%t.out 2>&1 + +// Test that (no_sanitize_memory) functions +// * don't check shadow values (-DCHECK_IN_F) +// * treat all values loaded from memory as fully initialized (-UCHECK_IN_F) + +#include <stdlib.h> +#include <stdio.h> + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(void) { + int x; + int * volatile p = &x; +#ifdef CHECK_IN_F + if (*p) + exit(0); +#endif + return *p; +} + +int main(void) { + if (f()) + exit(0); + return 0; +} diff --git a/test/msan/no_sanitize_memory_prop.cc b/test/msan/no_sanitize_memory_prop.cc new file mode 100644 index 000000000..355152478 --- /dev/null +++ b/test/msan/no_sanitize_memory_prop.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// Test that (no_sanitize_memory) functions propagate shadow. + +// Note that at -O0 there is no report, because 'x' in 'f' is spilled to the +// stack, and then loaded back as a fully initialiazed value (due to +// no_sanitize_memory attribute). + +#include <stdlib.h> +#include <stdio.h> + +__attribute__((noinline)) +__attribute__((no_sanitize_memory)) +int f(int x) { + return x; +} + +int main(void) { + int x; + int * volatile p = &x; + int y = f(*p); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*no_sanitize_memory_prop.cc:}}[[@LINE+1]] + if (y) + exit(0); + return 0; +} diff --git a/test/msan/poison_in_free.cc b/test/msan/poison_in_free.cc new file mode 100644 index 000000000..f134d05ab --- /dev/null +++ b/test/msan/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/test/msan/pthread_getattr_np_deadlock.cc b/test/msan/pthread_getattr_np_deadlock.cc new file mode 100644 index 000000000..44dfc19b9 --- /dev/null +++ b/test/msan/pthread_getattr_np_deadlock.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -fsanitize-memory-track-origins -O0 %s -o %t && %t + +// Regression test for a deadlock in pthread_getattr_np + +#include <assert.h> +#include <pthread.h> + +void *ThreadFn(void *) { + pthread_attr_t attr; + int res = pthread_getattr_np(pthread_self(), &attr); + assert(!res); + return 0; +} + +int main(void) { + pthread_t t; + int res = pthread_create(&t, 0, ThreadFn, 0); + assert(!res); + res = pthread_join(t, 0); + assert(!res); + return 0; +} diff --git a/test/msan/ptrace.cc b/test/msan/ptrace.cc new file mode 100644 index 000000000..d0e83eabd --- /dev/null +++ b/test/msan/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/test/msan/readdir64.cc b/test/msan/readdir64.cc new file mode 100644 index 000000000..0ec106c74 --- /dev/null +++ b/test/msan/readdir64.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %t + +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %t +// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %t + +// Test that readdir64 is intercepted as well as readdir. + +#include <sys/types.h> +#include <dirent.h> +#include <stdlib.h> + + +int main(void) { + DIR *dir = opendir("."); + struct dirent *d = readdir(dir); + if (d->d_name[0]) { + closedir(dir); + exit(0); + } + closedir(dir); + return 0; +} diff --git a/test/msan/scandir.cc b/test/msan/scandir.cc new file mode 100644 index 000000000..94672e1ad --- /dev/null +++ b/test/msan/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/test/msan/scandir_null.cc b/test/msan/scandir_null.cc new file mode 100644 index 000000000..84af7f418 --- /dev/null +++ b/test/msan/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/test/msan/scandir_test_root/aaa b/test/msan/scandir_test_root/aaa new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/scandir_test_root/aaa diff --git a/test/msan/scandir_test_root/aab b/test/msan/scandir_test_root/aab new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/scandir_test_root/aab diff --git a/test/msan/scandir_test_root/bbb b/test/msan/scandir_test_root/bbb new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/msan/scandir_test_root/bbb diff --git a/test/msan/select.cc b/test/msan/select.cc new file mode 100644 index 000000000..a169a2dd9 --- /dev/null +++ b/test/msan/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/test/msan/select_origin.cc b/test/msan/select_origin.cc new file mode 100644 index 000000000..f6f6a61b4 --- /dev/null +++ b/test/msan/select_origin.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %t 2>&1 | FileCheck %s + +// Test condition origin propagation through "select" IR instruction. + +#include <stdio.h> +#include <stdint.h> + +__attribute__((noinline)) +int *max_by_ptr(int *a, int *b) { + return *a < *b ? b : a; +} + +int main(void) { + int x; + int *volatile px = &x; + int y = 43; + int *p = max_by_ptr(px, &y); + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + return *p; +} diff --git a/test/msan/setlocale.cc b/test/msan/setlocale.cc new file mode 100644 index 000000000..a22b744d7 --- /dev/null +++ b/test/msan/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/test/msan/signal_stress_test.cc b/test/msan/signal_stress_test.cc new file mode 100644 index 000000000..ea75eae1b --- /dev/null +++ b/test/msan/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/test/msan/sigwait.cc b/test/msan/sigwait.cc new file mode 100644 index 000000000..29aa86c93 --- /dev/null +++ b/test/msan/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/test/msan/sigwaitinfo.cc b/test/msan/sigwaitinfo.cc new file mode 100644 index 000000000..d4f004598 --- /dev/null +++ b/test/msan/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/test/msan/stack-origin.cc b/test/msan/stack-origin.cc new file mode 100644 index 000000000..b0b05d965 --- /dev/null +++ b/test/msan/stack-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O1 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O2 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t && not %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + return *p; + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' + // CHECK-ORIGINS: {{#0 0x.* in main .*stack-origin.cc:}}[[@LINE-8]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin.cc:.* main}} +} diff --git a/test/msan/strerror_r-non-gnu.c b/test/msan/strerror_r-non-gnu.c new file mode 100644 index 000000000..8fb470558 --- /dev/null +++ b/test/msan/strerror_r-non-gnu.c @@ -0,0 +1,18 @@ +// RUN: %clang_msan -std=c99 -O0 -g %s -o %t && %t + +// strerror_r under a weird set of circumstances can be redirected to +// __xpg_strerror_r. Test that MSan handles this correctly. + +#define _POSIX_C_SOURCE 200112 +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +int main() { + char buf[1000]; + int res = strerror_r(EINVAL, buf, sizeof(buf)); + assert(!res); + volatile int z = strlen(buf); + return 0; +} diff --git a/test/msan/strlen_of_shadow.cc b/test/msan/strlen_of_shadow.cc new file mode 100644 index 000000000..cb2530e8f --- /dev/null +++ b/test/msan/strlen_of_shadow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +// Check that strlen() and similar intercepted functions can be called on shadow +// memory. + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +const char *mem_to_shadow(const char *p) { + return (char *)((uintptr_t)p & ~0x400000000000ULL); +} + +int main(void) { + const char *s = "abcdef"; + assert(strlen(s) == 6); + assert(strlen(mem_to_shadow(s)) == 0); + + char *t = new char[42]; + t[41] = 0; + assert(strlen(mem_to_shadow(t)) == 41); + return 0; +} diff --git a/test/msan/sync_lock_set_and_test.cc b/test/msan/sync_lock_set_and_test.cc new file mode 100644 index 000000000..1023b3e54 --- /dev/null +++ b/test/msan/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/test/msan/textdomain.cc b/test/msan/textdomain.cc new file mode 100644 index 000000000..e3968233a --- /dev/null +++ b/test/msan/textdomain.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t + +#include <libintl.h> +#include <stdio.h> + +int main() { + const char *td = textdomain("abcd"); + if (td[0] == 0) { + printf("Try read"); + } + return 0; +} diff --git a/test/msan/times.cc b/test/msan/times.cc new file mode 100644 index 000000000..1b7d00052 --- /dev/null +++ b/test/msan/times.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %t + +#include <assert.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/times.h> + + +int main(void) { + struct tms t; + clock_t res = times(&t); + assert(res != (clock_t)-1); + + if (t.tms_utime) printf("1\n"); + if (t.tms_stime) printf("2\n"); + if (t.tms_cutime) printf("3\n"); + if (t.tms_cstime) printf("4\n"); + + return 0; +} diff --git a/test/msan/tls_reuse.cc b/test/msan/tls_reuse.cc new file mode 100644 index 000000000..e1de7e87a --- /dev/null +++ b/test/msan/tls_reuse.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %t + +// Check that when TLS block is reused between threads, its shadow is cleaned. + +#include <pthread.h> +#include <stdio.h> + +int __thread x; + +void *ThreadFn(void *) { + if (!x) + printf("zzz\n"); + int y; + int * volatile p = &y; + x = *p; + return 0; +} + +int main(void) { + pthread_t t; + for (int i = 0; i < 100; ++i) { + pthread_create(&t, 0, ThreadFn, 0); + pthread_join(t, 0); + } + return 0; +} diff --git a/test/msan/tzset.cc b/test/msan/tzset.cc new file mode 100644 index 000000000..7e1c2cfad --- /dev/null +++ b/test/msan/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/test/msan/unaligned_read_origin.cc b/test/msan/unaligned_read_origin.cc new file mode 100644 index 000000000..fa29ab69d --- /dev/null +++ b/test/msan/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/test/msan/use-after-free.cc b/test/msan/use-after-free.cc new file mode 100644 index 000000000..ac47c0233 --- /dev/null +++ b/test/msan/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/test/msan/vector_cvt.cc b/test/msan/vector_cvt.cc new file mode 100644 index 000000000..c200c77de --- /dev/null +++ b/test/msan/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/test/msan/vector_select.cc b/test/msan/vector_select.cc new file mode 100644 index 000000000..e8d554232 --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls.cc b/test/msan/wrap_indirect_calls.cc new file mode 100644 index 000000000..b4bac1ecb --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls/caller.cc b/test/msan/wrap_indirect_calls/caller.cc new file mode 100644 index 000000000..a0af8b7bb --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls/lit.local.cfg b/test/msan/wrap_indirect_calls/lit.local.cfg new file mode 100644 index 000000000..5e01230c0 --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls/one.cc b/test/msan/wrap_indirect_calls/one.cc new file mode 100644 index 000000000..ab7bf4125 --- /dev/null +++ b/test/msan/wrap_indirect_calls/one.cc @@ -0,0 +1,3 @@ +int f_other_object(int x, int y) { + return x * y; +} diff --git a/test/msan/wrap_indirect_calls/two.cc b/test/msan/wrap_indirect_calls/two.cc new file mode 100644 index 000000000..c939a993b --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls/wrapper.cc b/test/msan/wrap_indirect_calls/wrapper.cc new file mode 100644 index 000000000..8fcd0c635 --- /dev/null +++ b/test/msan/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/test/msan/wrap_indirect_calls2.cc b/test/msan/wrap_indirect_calls2.cc new file mode 100644 index 000000000..c188047ce --- /dev/null +++ b/test/msan/wrap_indirect_calls2.cc @@ -0,0 +1,42 @@ +// Test __msan_set_indirect_call_wrapper. + +// RUN: %clangxx_msan -mllvm -msan-wrap-indirect-calls=__msan_wrap_indirect_call \ +// RUN: -mllvm -msan-wrap-indirect-calls-fast=0 \ +// RUN: -O0 -g -rdynamic -Wl,--defsym=__executable_start=0 %s -o %t && %t + +// This test disables -msan-wrap-indirect-calls-fast, otherwise indirect calls +// inside the same module are short-circuited and are never seen by the wrapper. + +#include <assert.h> +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> + +extern "C" void __msan_set_indirect_call_wrapper(uintptr_t); + +bool done_f, done_g; + +void f(void) { + assert(!done_g); + done_f = true; +} + +void g(void) { + assert(done_f); + done_g = true; +} + +typedef void (*Fn)(void); +extern "C" Fn my_wrapper(Fn target) { + if (target == f) return g; + return target; +} + +int main(void) { + volatile Fn fp; + fp = &f; + fp(); + __msan_set_indirect_call_wrapper((uintptr_t)my_wrapper); + fp(); + return !(done_f && done_g); +} diff --git a/test/msan/wrap_indirect_calls_in_rtl.cc b/test/msan/wrap_indirect_calls_in_rtl.cc new file mode 100644 index 000000000..0d9051ba7 --- /dev/null +++ b/test/msan/wrap_indirect_calls_in_rtl.cc @@ -0,0 +1,80 @@ +// Test indirect call wrapping in MemorySanitizer runtime. + +// RUN: %clangxx_msan -O0 -g -rdynamic %s -o %t && %t + +#include <assert.h> +#include <math.h> +#include <pthread.h> +#include <stdio.h> +#include <stdint.h> +#include <sys/time.h> + +extern "C" void __msan_set_indirect_call_wrapper(uintptr_t); + +bool pthread_create_done; + +void *ThreadFn(void *) { + printf("bad threadfn\n"); + return 0; +} + +void *ThreadFn2(void *) { + printf("good threadfn\n"); + pthread_create_done = true; + return 0; +} + +bool in_gettimeofday; +bool in_lgamma; + +int my_gettimeofday(struct timeval *p, void *q) { + p->tv_sec = 1; + p->tv_usec = 2; + return 42; +} + +double my_lgamma(double x) { + printf("zzz\n"); + return x; +} + +extern "C" uintptr_t my_wrapper(uintptr_t f) { + if (f == (uintptr_t)ThreadFn) + return (uintptr_t)&ThreadFn2; + if (in_gettimeofday) + return (uintptr_t)my_gettimeofday; + if (in_lgamma) + return (uintptr_t)my_lgamma; + return f; +} + +int main(void) { + __msan_set_indirect_call_wrapper((uintptr_t)my_wrapper); + + // ThreadFn is called indirectly from a wrapper function in MSan rtl and + // is subject to indirect call wrapping (it could be an native-to-translated + // edge). + pthread_t t; + pthread_create(&t, 0, ThreadFn, 0); + pthread_join(t, 0); + assert(pthread_create_done); + + // gettimeofday is intercepted in msan_interceptors.cc and the real one (from + // libc) is called indirectly. + struct timeval tv; + in_gettimeofday = true; + int res = gettimeofday(&tv, NULL); + in_gettimeofday = false; + assert(tv.tv_sec == 1); + assert(tv.tv_usec == 2); + assert(res == 42); + + // lgamma is intercepted in sanitizer_common_interceptors.inc and is also + // called indirectly. + in_lgamma = true; + double dres = lgamma(1.1); + in_lgamma = false; + assert(dres == 1.1); + + return 0; +} |