diff options
Diffstat (limited to 'test/msan')
101 files changed, 3309 insertions, 0 deletions
diff --git a/test/msan/CMakeLists.txt b/test/msan/CMakeLists.txt new file mode 100644 index 0000000000000..08786ee777ebd --- /dev/null +++ b/test/msan/CMakeLists.txt @@ -0,0 +1,23 @@ +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}) +if(NOT COMPILER_RT_STANDALONE_BUILD) + list(APPEND MSAN_TEST_DEPS msan) +endif() + +if(COMPILER_RT_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 0000000000000..385351dfdcde8 --- /dev/null +++ b/test/msan/Linux/getresid.cc @@ -0,0 +1,25 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..8604e4d76265b --- /dev/null +++ b/test/msan/Linux/glob.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..2c02e735e05e4 --- /dev/null +++ b/test/msan/Linux/glob_altdirfunc.cc @@ -0,0 +1,78 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..bc35c30d6d07f --- /dev/null +++ b/test/msan/Linux/glob_nomatch.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..e69de29bb2d1d --- /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 0000000000000..e69de29bb2d1d --- /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 0000000000000..e69de29bb2d1d --- /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 0000000000000..57271b8078a49 --- /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/sunrpc.cc b/test/msan/Linux/sunrpc.cc new file mode 100644 index 0000000000000..78645a7dcbf2a --- /dev/null +++ b/test/msan/Linux/sunrpc.cc @@ -0,0 +1,40 @@ +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=int -DFN=xdr_int -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=double -DFN=xdr_double -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DTYPE=u_quad_t -DFN=xdr_u_longlong_t -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + TYPE x; +#ifndef UNINIT + x = 42; +#endif + bool_t res = FN(&xdrs, &x); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + TYPE y; + res = FN(&xdrs, &y); + assert(res == TRUE); + assert(__msan_test_shadow(&y, sizeof(y)) == -1); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/sunrpc_bytes.cc b/test/msan/Linux/sunrpc_bytes.cc new file mode 100644 index 0000000000000..f0c35746f34dd --- /dev/null +++ b/test/msan/Linux/sunrpc_bytes.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <string.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + char s[20]; +#ifndef UNINIT + strcpy(s, "hello"); +#endif + char *sp = s; + unsigned sz = 6; + bool_t res = xdr_bytes(&xdrs, &sp, &sz, sizeof(s)); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc_bytes.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + char s2[20]; + char *sp2 = s2; + unsigned sz2; + res = xdr_bytes(&xdrs, &sp2, &sz2, sizeof(s2)); + assert(res == TRUE); + assert(sz == sz2); + assert(strcmp(s, s2) == 0); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/sunrpc_string.cc b/test/msan/Linux/sunrpc_string.cc new file mode 100644 index 0000000000000..3f44a96d114cb --- /dev/null +++ b/test/msan/Linux/sunrpc_string.cc @@ -0,0 +1,35 @@ +// RUN: %clangxx_msan -m64 -g -O0 %s -o %t && \ +// RUN: %run %t 2>&1 +// RUN: %clangxx_msan -m64 -g -O0 -DUNINIT=1 %s -o %t && \ +// RUN: not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <string.h> +#include <rpc/xdr.h> + +#include <sanitizer/msan_interface.h> + +int main(int argc, char *argv[]) { + XDR xdrs; + char buf[100]; + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_ENCODE); + char s[20]; +#ifndef UNINIT + strcpy(s, "hello"); +#endif + char *sp = s; + bool_t res = xdr_string(&xdrs, &sp, sizeof(s)); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{in main.*sunrpc_string.cc:}}[[@LINE-2]] + assert(res == TRUE); + xdr_destroy(&xdrs); + + xdrmem_create(&xdrs, buf, sizeof(buf), XDR_DECODE); + char s2[20]; + char *sp2 = s2; + res = xdr_string(&xdrs, &sp2, sizeof(s2)); + assert(res == TRUE); + assert(strcmp(s, s2) == 0); + xdr_destroy(&xdrs); + return 0; +} diff --git a/test/msan/Linux/syscalls.cc b/test/msan/Linux/syscalls.cc new file mode 100644 index 0000000000000..4dd97e7451480 --- /dev/null +++ b/test/msan/Linux/syscalls.cc @@ -0,0 +1,115 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 <sys/uio.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[3]; + struct iocb *iocbp[3] = { &iocb[0], &iocb[1], &iocb[2] }; + memset(iocb, 0, sizeof(iocb)); + iocb[0].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[0].aio_buf = (__u64)buf; + iocb[0].aio_nbytes = 10; + iocb[1].aio_lio_opcode = IOCB_CMD_PREAD; + iocb[1].aio_buf = (__u64)(&buf[20]); + iocb[1].aio_nbytes = 15; + struct iovec vec[2] = { {&buf[40], 3}, {&buf[50], 20} }; + iocb[2].aio_lio_opcode = IOCB_CMD_PREADV; + iocb[2].aio_buf = (__u64)(&vec); + iocb[2].aio_nbytes = 2; + __sanitizer_syscall_pre_io_submit(0, 3, &iocbp); + assert(__msan_test_shadow(buf, sizeof(buf)) == 10); + assert(__msan_test_shadow(buf + 20, sizeof(buf) - 20) == 15); + assert(__msan_test_shadow(buf + 40, sizeof(buf) - 40) == 3); + assert(__msan_test_shadow(buf + 50, sizeof(buf) - 50) == 20); + + __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 0000000000000..e1425b84f5504 --- /dev/null +++ b/test/msan/Linux/tcgetattr.cc @@ -0,0 +1,21 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..1beba117d574b --- /dev/null +++ b/test/msan/Linux/xattr.cc @@ -0,0 +1,145 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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); + size_t argv_size = (attr_list_len + 1) * sizeof(char *); + char **attrs = (char **)malloc(argv_size); + argz_extract(attr_list, attr_list_size, attrs); + // TODO(smatveev): we need proper argz_* interceptors + __msan_unpoison(attrs, argv_size); + for (size_t 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 0000000000000..e69de29bb2d1d --- /dev/null +++ b/test/msan/Linux/xattr_test_root/a diff --git a/test/msan/Unit/lit.site.cfg.in b/test/msan/Unit/lit.site.cfg.in new file mode 100644 index 0000000000000..dc0e9613d59ec --- /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 0000000000000..f4ea51d588726 --- /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 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %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 0000000000000..473e0ae8f88b7 --- /dev/null +++ b/test/msan/backtrace.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..059300e4205ab --- /dev/null +++ b/test/msan/c-strdup.c @@ -0,0 +1,17 @@ +// RUN: %clang_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clang_msan -m64 -O3 %s -o %t && %run %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/chained_origin.cc b/test/msan/chained_origin.cc new file mode 100644 index 0000000000000..336bbd852cb33 --- /dev/null +++ b/test/msan/chained_origin.cc @@ -0,0 +1,66 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out + + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-STACK < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DHEAP=1 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-HEAP < %t.out + + +#include <stdio.h> + +volatile int x, y; + +__attribute__((noinline)) +void fn_g(int a) { + x = a; +} + +__attribute__((noinline)) +void fn_f(int a) { + fn_g(a); +} + +__attribute__((noinline)) +void fn_h() { + y = x; +} + +int main(int argc, char *argv[]) { +#ifdef HEAP + int * volatile zz = new int; + int z = *zz; +#else + int volatile z; +#endif + fn_f(z); + fn_h(); + return y; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: {{#0 .* in main.*chained_origin.cc:47}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_h.*chained_origin.cc:35}} +// CHECK: {{#1 .* in main.*chained_origin.cc:46}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_g.*chained_origin.cc:25}} +// CHECK: {{#1 .* in fn_f.*chained_origin.cc:30}} +// CHECK: {{#2 .* in main.*chained_origin.cc:45}} + +// CHECK-STACK: Uninitialized value was created by an allocation of 'z' in the stack frame of function 'main' +// CHECK-STACK: {{#0 .* in main.*chained_origin.cc:38}} + +// CHECK-HEAP: Uninitialized value was created by a heap allocation +// CHECK-HEAP: {{#1 .* in main.*chained_origin.cc:40}} diff --git a/test/msan/chained_origin_empty_stack.cc b/test/msan/chained_origin_empty_stack.cc new file mode 100644 index 0000000000000..36727e3d7aa74 --- /dev/null +++ b/test/msan/chained_origin_empty_stack.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: MSAN_OPTIONS=store_context_size=1 not %run %t 2>&1 | FileCheck %s + +// Test that stack trace for the intermediate store is not empty. + +// CHECK: MemorySanitizer: use-of-uninitialized-value +// CHECK: #0 {{.*}} in main + +// CHECK: Uninitialized value was stored to memory at +// CHECK: #0 {{.*}} in fn_g +// CHECK-NOT: #1 + +// CHECK: Uninitialized value was created by an allocation of 'z' in the stack frame of function 'main' +// CHECK: #0 {{.*}} in main + +#include <stdio.h> + +volatile int x; + +__attribute__((noinline)) +void fn_g(int a) { + x = a; +} + +__attribute__((noinline)) +void fn_f(int a) { + fn_g(a); +} + +int main(int argc, char *argv[]) { + int volatile z; + fn_f(z); + return x; +} diff --git a/test/msan/chained_origin_limits.cc b/test/msan/chained_origin_limits.cc new file mode 100644 index 0000000000000..0cc57f32a6ac1 --- /dev/null +++ b/test/msan/chained_origin_limits.cc @@ -0,0 +1,178 @@ +// This test program creates a very large number of unique histories. + +// Heap origin. +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// Stack origin. +// RUN: %clangxx_msan -DSTACK -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + + +// Heap origin, with calls. +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + + +// Stack origin, with calls. +// RUN: %clangxx_msan -DSTACK -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t + +// RUN: MSAN_OPTIONS=origin_history_size=7 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=2 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK2 < %t.out + +// RUN: MSAN_OPTIONS=origin_history_per_stack_limit=1 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK-PER-STACK < %t.out + +// RUN: MSAN_OPTIONS=origin_history_size=7,origin_history_per_stack_limit=0 not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK7 < %t.out + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static char *buf, *cur, *end; +void init() { + buf = new char[1000]; +#ifdef STACK + char stackbuf[1000]; + char *volatile p = stackbuf; + memcpy(buf, p, 1000); +#endif + cur = buf; + end = buf + 1000; +} + +void line_flush() { + char *p; + for (p = cur - 1; p >= buf; --p) + if (*p == '\n') + break; + if (p >= buf) { + size_t write_sz = p - buf + 1; + // write(2, buf, write_sz); + memmove(buf, p + 1, end - p - 1); + cur -= write_sz; + } +} + +void buffered_write(const char *p, size_t sz) { + while (sz > 0) { + size_t copy_sz = end - cur; + if (sz < copy_sz) copy_sz = sz; + memcpy(cur, p, copy_sz); + cur += copy_sz; + sz -= copy_sz; + line_flush(); + } +} + +void fn1() { + buffered_write("a\n", 2); +} + +void fn2() { + buffered_write("a\n", 2); +} + +void fn3() { + buffered_write("a\n", 2); +} + +int main(void) { + init(); + for (int i = 0; i < 2000; ++i) { + fn1(); + fn2(); + fn3(); + } + return buf[50]; +} + +// CHECK7: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was stored to memory at +// CHECK7-NOT: Uninitialized value was stored to memory at +// CHECK7: Uninitialized value was created + +// CHECK2: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK2-NOT: Uninitialized value was stored to memory at +// CHECK2: Uninitialized value was stored to memory at +// CHECK2-NOT: Uninitialized value was stored to memory at +// CHECK2: Uninitialized value was created + +// CHECK-PER-STACK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn3 +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn2 +// CHECK-PER-STACK: Uninitialized value was stored to memory at +// CHECK-PER-STACK: in fn1 +// CHECK-PER-STACK: Uninitialized value was created + +// CHECK-UNLIMITED: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was stored to memory at +// CHECK-UNLIMITED: Uninitialized value was created diff --git a/test/msan/chained_origin_memcpy.cc b/test/msan/chained_origin_memcpy.cc new file mode 100644 index 0000000000000..f4c2f7fcac876 --- /dev/null +++ b/test/msan/chained_origin_memcpy.cc @@ -0,0 +1,61 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out + + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -DOFFSET=0 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z1 < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -DOFFSET=10 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-Z2 < %t.out + + +#include <stdio.h> +#include <string.h> + +int xx[10000]; +int yy[10000]; +volatile int idx = 30; + +__attribute__((noinline)) +void fn_g(int a, int b) { + xx[idx] = a; xx[idx + 10] = b; +} + +__attribute__((noinline)) +void fn_f(int a, int b) { + fn_g(a, b); +} + +__attribute__((noinline)) +void fn_h() { + memcpy(&yy, &xx, sizeof(xx)); +} + +int main(int argc, char *argv[]) { + int volatile z1; + int volatile z2; + fn_f(z1, z2); + fn_h(); + return yy[idx + OFFSET]; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK: {{#0 .* in main .*chained_origin_memcpy.cc:46}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#1 .* in fn_h.*chained_origin_memcpy.cc:38}} + +// CHECK: Uninitialized value was stored to memory at +// CHECK: {{#0 .* in fn_g.*chained_origin_memcpy.cc:28}} +// CHECK: {{#1 .* in fn_f.*chained_origin_memcpy.cc:33}} + +// CHECK-Z1: Uninitialized value was created by an allocation of 'z1' in the stack frame of function 'main' +// CHECK-Z2: Uninitialized value was created by an allocation of 'z2' in the stack frame of function 'main' +// CHECK: {{#0 .* in main.*chained_origin_memcpy.cc:41}} diff --git a/test/msan/chained_origin_with_signals.cc b/test/msan/chained_origin_with_signals.cc new file mode 100644 index 0000000000000..2841e34a1f1da --- /dev/null +++ b/test/msan/chained_origin_with_signals.cc @@ -0,0 +1,36 @@ +// Check that stores in signal handlers are not recorded in origin history. +// This is, in fact, undesired behavior caused by our chained origins +// implementation being not async-signal-safe. + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -mllvm -msan-instrumentation-with-call-threshold=0 -fsanitize-memory-track-origins=2 -m64 -O3 %s -o %t && \ +// RUN: not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <signal.h> +#include <stdio.h> +#include <sys/types.h> +#include <unistd.h> + +volatile int x, y; + +void SignalHandler(int signo) { + y = x; +} + +int main(int argc, char *argv[]) { + int volatile z; + x = z; + + signal(SIGHUP, SignalHandler); + kill(getpid(), SIGHUP); + signal(SIGHUP, SIG_DFL); + + return y; +} + +// CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK-NOT: in SignalHandler diff --git a/test/msan/check_mem_is_initialized.cc b/test/msan/check_mem_is_initialized.cc new file mode 100644 index 0000000000000..7d2328810d902 --- /dev/null +++ b/test/msan/check_mem_is_initialized.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <sanitizer/msan_interface.h> +#include <stdlib.h> + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + + __msan_check_mem_is_initialized(p, sizeof(*p)); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*check_mem_is_initialized.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 .*check_mem_is_initialized.cc:}}[[@LINE-8]] + return 0; +} diff --git a/test/msan/coverage-levels.cc b/test/msan/coverage-levels.cc new file mode 100644 index 0000000000000..7c2e143d3ab8a --- /dev/null +++ b/test/msan/coverage-levels.cc @@ -0,0 +1,28 @@ +// Test various levels of coverage +// +// RUN: %clangxx_msan -DINIT_VAR=1 -O1 -fsanitize-coverage=1 %s -o %t +// RUN: mkdir -p %T/coverage-levels +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_NOWARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=1 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK1 --check-prefix=CHECK_WARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=2 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK2 --check-prefix=CHECK_WARN +// RUN: %clangxx_msan -O1 -fsanitize-coverage=3 %s -o %t +// RUN: MSAN_OPTIONS=coverage=1:verbosity=1:coverage_dir=%T/coverage-levels not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK3 --check-prefix=CHECK_WARN +// +volatile int sink; +int main(int argc, char **argv) { + int var; +#if INIT_VAR + var = 0; +#endif + if (argc == 0) + sink = 0; + return *(volatile int*)&var; +} + +// CHECK_WARN: WARNING: MemorySanitizer: use-of-uninitialized-value +// CHECK_NOWARN-NOT: ERROR +// CHECK1: 1 PCs written +// CHECK2: 2 PCs written +// CHECK3: 3 PCs written diff --git a/test/msan/cxa_atexit.cc b/test/msan/cxa_atexit.cc new file mode 100644 index 0000000000000..0aa36ecee0117 --- /dev/null +++ b/test/msan/cxa_atexit.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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/death-callback.cc b/test/msan/death-callback.cc new file mode 100644 index 0000000000000..6d04883399987 --- /dev/null +++ b/test/msan/death-callback.cc @@ -0,0 +1,39 @@ +// RUN: %clangxx_msan -m64 -DERROR %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB +// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-CB +// RUN: %clangxx_msan -m64 -DERROR -DMSANCB_SET -DMSANCB_CLEAR %s -o %t && not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB +// RUN: %clangxx_msan -m64 -DMSANCB_SET %s -o %t && %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCB + +#include <sanitizer/msan_interface.h> +#include <stdio.h> +#include <stdlib.h> + +void cb(void) { + fprintf(stderr, "msan-death-callback\n"); +} + +int main(int argc, char **argv) { + int *volatile p = (int *)malloc(sizeof(int)); + *p = 42; + free(p); + +#ifdef MSANCB_SET + __msan_set_death_callback(cb); +#endif + +#ifdef MSANCB_CLEAR + __msan_set_death_callback(0); +#endif + +#ifdef ERROR + if (*p) + exit(0); +#endif + // CHECK-CB: msan-death-callback + // CHECK-NOCB-NOT: msan-death-callback + fprintf(stderr, "done\n"); + return 0; +} diff --git a/test/msan/default_blacklist.cc b/test/msan/default_blacklist.cc new file mode 100644 index 0000000000000..32cc02257cb09 --- /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 0000000000000..2c726d36041ef --- /dev/null +++ b/test/msan/dlerror.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..ba008c00718d1 --- /dev/null +++ b/test/msan/dso-origin.cc @@ -0,0 +1,48 @@ +// 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 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_msan -m64 -O0 %s %t-so.so -o %t && not %run %t 2>&1 | FileCheck %s + +#ifdef BUILD_SO + +#include <stdlib.h> + +extern "C" { +void my_access(int *p) { + volatile int tmp; + // Force initialize-ness check. + if (*p) + tmp = 1; +} + +void *my_alloc(unsigned sz) { + return malloc(sz); +} +} // extern "C" + +#else // BUILD_SO + +#include <stdlib.h> + +extern "C" { +void my_access(int *p); +void *my_alloc(unsigned sz); +} + +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.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.cc:}} + // CHECK: {{#2 0x.* in main .*dso-origin.cc:}}[[@LINE-10]] + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*dso-origin.cc:.* my_access}} + return 0; +} + +#endif // BUILD_SO diff --git a/test/msan/dtls_test.c b/test/msan/dtls_test.c new file mode 100644 index 0000000000000..cb88ede2f0a60 --- /dev/null +++ b/test/msan/dtls_test.c @@ -0,0 +1,60 @@ +/* RUN: %clang_msan -g -m64 %s -o %t + RUN: %clang_msan -g -m64 %s -DBUILD_SO -fPIC -o %t-so.so -shared + RUN: %run %t 2>&1 + + Regression test for a bug in msan/glibc integration, + see https://sourceware.org/bugzilla/show_bug.cgi?id=16291 + and https://code.google.com/p/memory-sanitizer/issues/detail?id=44 +*/ + +#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 0000000000000..8af8eb5ee6f93 --- /dev/null +++ b/test/msan/errno.cc @@ -0,0 +1,17 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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/fork.cc b/test/msan/fork.cc new file mode 100644 index 0000000000000..10de8a9379d8a --- /dev/null +++ b/test/msan/fork.cc @@ -0,0 +1,121 @@ +// Test that chained origins are fork-safe. +// Run a number of threads that create new chained origins, then fork +// and verify that origin reads do not deadlock in the child process. + +// RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -m64 -O3 %s -o %t +// RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s + +// Fun fact: if test output is redirected to a file (as opposed to +// being piped directly to FileCheck), we may lose some "done"s due to +// a kernel bug: +// https://lkml.org/lkml/2014/2/17/324 + + +#include <pthread.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <signal.h> +#include <errno.h> + +#include <sanitizer/msan_interface.h> + +int done; + +void copy_uninit_thread2() { + volatile int x; + volatile int v; + while (true) { + v = x; + x = v; + if (__atomic_load_n(&done, __ATOMIC_RELAXED)) + return; + } +} + +void copy_uninit_thread1(int level) { + if (!level) + copy_uninit_thread2(); + else + copy_uninit_thread1(level - 1); +} + +void *copy_uninit_thread(void *id) { + copy_uninit_thread1((long)id); + return 0; +} + +// Run through stackdepot in the child process. +// If any of the hash table cells are locked, this may deadlock. +void child() { + volatile int x; + volatile int v; + for (int i = 0; i < 10000; ++i) { + v = x; + x = v; + } + write(2, "done\n", 5); +} + +void test() { + const int kThreads = 10; + pthread_t t[kThreads]; + for (int i = 0; i < kThreads; ++i) + pthread_create(&t[i], NULL, copy_uninit_thread, (void*)(long)i); + usleep(100000); + pid_t pid = fork(); + if (pid) { + // parent + __atomic_store_n(&done, 1, __ATOMIC_RELAXED); + pid_t p; + while ((p = wait(NULL)) == -1) { } + } else { + // child + child(); + } +} + +int main() { + const int kChildren = 20; + for (int i = 0; i < kChildren; ++i) { + pid_t pid = fork(); + if (pid) { + // parent + } else { + test(); + exit(0); + } + } + + for (int i = 0; i < kChildren; ++i) { + pid_t p; + while ((p = wait(NULL)) == -1) { } + } + + return 0; +} + +// Expect 20 (== kChildren) "done" messages. +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done diff --git a/test/msan/ftime.cc b/test/msan/ftime.cc new file mode 100644 index 0000000000000..2d0935d18037a --- /dev/null +++ b/test/msan/ftime.cc @@ -0,0 +1,14 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sys/timeb.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct timeb tb; + int res = ftime(&tb); + assert(!res); + assert(__msan_test_shadow(&tb, sizeof(tb)) == -1); + return 0; +} diff --git a/test/msan/getaddrinfo-positive.cc b/test/msan/getaddrinfo-positive.cc new file mode 100644 index 0000000000000..7658cd504dba0 --- /dev/null +++ b/test/msan/getaddrinfo-positive.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %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: Uninitialized bytes in __interceptor_getaddrinfo at offset 0 inside [0x{{.*}}, 48) + // 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 0000000000000..abab8bd78f750 --- /dev/null +++ b/test/msan/getaddrinfo.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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/getc_unlocked.c b/test/msan/getc_unlocked.c new file mode 100644 index 0000000000000..7df958ad657a5 --- /dev/null +++ b/test/msan/getc_unlocked.c @@ -0,0 +1,32 @@ +// RUN: %clangxx_msan -DGETC -m64 -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETC -m64 -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETC -m64 -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETC -m64 -O3 -g %s -o %t && %run %t + +// RUN: %clangxx_msan -DGETCHAR -m64 -O0 -g -xc++ %s -o %t && %run %t +// RUN: %clangxx_msan -DGETCHAR -m64 -O3 -g -xc++ %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -m64 -O0 -g %s -o %t && %run %t +// RUN: %clang_msan -DGETCHAR -m64 -O3 -g %s -o %t && %run %t + +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +int main() { + FILE *stream = fopen("/dev/zero", "r"); + flockfile (stream); + int c; +#if defined(GETCHAR) + int res = dup2(fileno(stream), 0); + assert(res == 0); + c = getchar_unlocked(); +#elif defined(GETC) + c = getc_unlocked (stream); +#endif + funlockfile (stream); + if (c == EOF) + return 1; + printf("%c\n", (char)c); + fclose(stream); + return 0; +} diff --git a/test/msan/getline.cc b/test/msan/getline.cc new file mode 100644 index 0000000000000..51e105e0be5c5 --- /dev/null +++ b/test/msan/getline.cc @@ -0,0 +1,36 @@ +// RUN: echo "abcde" > %t-testdata +// RUN: echo "12345" >> %t-testdata +// RUN: %clangxx_msan -O0 %s -o %t && %run %t %t-testdata +// RUN: %clangxx_msan -O2 %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O0 -xc %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O2 -xc %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O0 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata +// RUN: %clang_msan -O2 -xc -D_GNU_SOURCE=1 %s -o %t && %run %t %t-testdata + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main(int argc, char **argv) { + assert(argc == 2); + printf("%s\n", argv[1]); + + FILE *fp = fopen(argv[1], "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/heap-origin.cc b/test/msan/heap-origin.cc new file mode 100644 index 0000000000000..0920001beed71 --- /dev/null +++ b/test/msan/heap-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %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 0000000000000..ea6958b79b967 --- /dev/null +++ b/test/msan/iconv.cc @@ -0,0 +1,48 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %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 0000000000000..b74aec16c98ae --- /dev/null +++ b/test/msan/if_indextoname.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..6a0db3a5b71fe --- /dev/null +++ b/test/msan/ifaddrs.cc @@ -0,0 +1,50 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..94f6cd8252f38 --- /dev/null +++ b/test/msan/initgroups.cc @@ -0,0 +1,11 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..b2fa961508569 --- /dev/null +++ b/test/msan/inline.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -O3 %s -o %t && %run %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 0000000000000..545debffaad50 --- /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 %run %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 %run %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 0000000000000..caa5c274f5eba --- /dev/null +++ b/test/msan/ioctl.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %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 0000000000000..7c7fde4bd5d02 --- /dev/null +++ b/test/msan/ioctl_custom.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %t + +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 -g %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -DPOSITIVE -m64 -O3 -g %s -o %t && not %run %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: Uninitialized bytes in ioctl{{.*}} at offset 0 inside [0x{{.*}}, 4) + // 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 0000000000000..ed896f761181f --- /dev/null +++ b/test/msan/ioctl_sound.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -g %s -o %t && %run %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 0000000000000..7975306c557fb --- /dev/null +++ b/test/msan/keep-going-dso.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %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 %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %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 %run %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 %run %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 %run %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 0000000000000..6426574a9e502 --- /dev/null +++ b/test/msan/keep-going.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=0 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -m64 -O0 %s -o %t && MSAN_OPTIONS=keep_going=1 not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out + +// RUN: %clangxx_msan -m64 -mllvm -msan-keep-going=1 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %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 %run %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 0000000000000..f425e25957ac1 --- /dev/null +++ b/test/msan/lit.cfg @@ -0,0 +1,30 @@ +# -*- 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", + "-m64"] + config.debug_info_flags +clang_msan_cxxflags = config.cxx_mode_flags + clang_msan_cflags + +def build_invocation(compile_flags): + return " " + " ".join([config.clang] + compile_flags) + " " + +config.substitutions.append( ("%clang_msan ", build_invocation(clang_msan_cflags)) ) +config.substitutions.append( ("%clangxx_msan ", build_invocation(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 0000000000000..fb22a57a9e665 --- /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/mallinfo.cc b/test/msan/mallinfo.cc new file mode 100644 index 0000000000000..3f308683077ef --- /dev/null +++ b/test/msan/mallinfo.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <malloc.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct mallinfo mi = mallinfo(); + assert(__msan_test_shadow(&mi, sizeof(mi)) == -1); + return 0; +} diff --git a/test/msan/mktime.cc b/test/msan/mktime.cc new file mode 100644 index 0000000000000..c419057c39079 --- /dev/null +++ b/test/msan/mktime.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <time.h> + +#include <sanitizer/msan_interface.h> + +int main(void) { + struct tm tm; + tm.tm_year = 2014; + tm.tm_mon = 3; + tm.tm_mday = 28; +#ifndef UNINIT + tm.tm_hour = 13; +#endif + tm.tm_min = 4; + tm.tm_sec = 42; + tm.tm_isdst = -1; + time_t t = mktime(&tm); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: in main{{.*}}mktime.cc:[[@LINE-2]] + assert(t != -1); + assert(__msan_test_shadow(&tm, sizeof(tm)) == -1); + return 0; +} diff --git a/test/msan/mmap_below_shadow.cc b/test/msan/mmap_below_shadow.cc new file mode 100644 index 0000000000000..4b5890ba0fb83 --- /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 && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -DFIXED=1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %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/msan_check_mem_is_initialized.cc b/test/msan/msan_check_mem_is_initialized.cc new file mode 100644 index 0000000000000..33558cd2feb27 --- /dev/null +++ b/test/msan/msan_check_mem_is_initialized.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s --check-prefix=CHECK +// RUN: MSAN_OPTIONS=verbosity=1 not %run %t 2>&1 | \ +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-VERBOSE + +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> + +int main(void) { + char p[32] = {}; + __msan_poison(p + 10, 2); + + __msan_check_mem_is_initialized(p, 10); + __msan_check_mem_is_initialized(p + 12, 30); +#ifdef POSITIVE + __msan_check_mem_is_initialized(p + 5, 20); + // CHECK: Uninitialized bytes in __msan_check_mem_is_initialized at offset 5 inside [0x{{.*}}, 20) + // CHECK-VERBOSE: Shadow map of [0x{{.*}}, 0x{{.*}}), 20 bytes: + // CHECK-VERBOSE: 0x{{.*}}: ..000000 0000ffff 00000000 00000000 + // CHECK-VERBOSE: 0x{{.*}}: 00000000 00...... ........ ........ + + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 0x{{.*}}in main{{.*}}msan_check_mem_is_initialized.cc:[[@LINE-7]] +#endif + return 0; +} + diff --git a/test/msan/msan_dump_shadow.cc b/test/msan/msan_dump_shadow.cc new file mode 100644 index 0000000000000..08371e306f32a --- /dev/null +++ b/test/msan/msan_dump_shadow.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char *p = new char[16]; + __msan_dump_shadow(p, 5); + delete[] p; + const char *q = "abc"; + __msan_dump_shadow(q, 3); + return 0; +} + +// CHECK: ff ff ff ff ff +// CHECK: 00 00 00 diff --git a/test/msan/msan_print_shadow.cc b/test/msan/msan_print_shadow.cc new file mode 100644 index 0000000000000..0cc1d660be1b6 --- /dev/null +++ b/test/msan/msan_print_shadow.cc @@ -0,0 +1,122 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS --check-prefix=CHECK-ORIGINS-2 < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char volatile x; + char *p = new char[320]; + p[2] = p[5] = 1; + p[8] = p[9] = p[10] = p[11] = p[12] = 2; + + __msan_allocated_memory(p + 4*3, 4); + __msan_allocated_memory(p + 4*4, 4); + __msan_allocated_memory(p + 4*5, 4); + __msan_allocated_memory(p + 4*6, 4); + __msan_allocated_memory(p + 4*7, 4); + __msan_allocated_memory(p + 4*8, 4); + __msan_allocated_memory(p + 4*9, 4); + __msan_allocated_memory(p + 4*10, 4); + __msan_allocated_memory(p + 4*11, 4); + __msan_allocated_memory(p + 4*12, 4); + __msan_allocated_memory(p + 4*13, 4); + __msan_allocated_memory(p + 4*14, 4); + __msan_allocated_memory(p + 4*15, 4); + __msan_allocated_memory(p + 4*16, 4); + __msan_allocated_memory(p + 4*17, 4); + __msan_allocated_memory(p + 4*18, 4); + __msan_allocated_memory(p + 4*19, 4); + __msan_allocated_memory(p + 4*20, 4); + __msan_allocated_memory(p + 4*21, 4); + __msan_allocated_memory(p + 4*22, 4); + __msan_allocated_memory(p + 4*23, 4); + __msan_allocated_memory(p + 4*24, 4); + __msan_allocated_memory(p + 4*25, 4); + __msan_allocated_memory(p + 4*26, 4); + __msan_allocated_memory(p + 4*27, 4); + __msan_allocated_memory(p + 4*28, 4); + __msan_allocated_memory(p + 4*29, 4); + __msan_allocated_memory(p + 4*30, 4); + __msan_allocated_memory(p + 4*31, 4); + + p[19] = x; + + __msan_print_shadow(p+5, 297); + delete[] p; + return 0; +} + +// CHECK: Shadow map of [{{.*}}), 297 bytes: + +// CHECK-NO-ORIGINS: 0x{{.*}}: ..00ffff 00000000 ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff +// CHECK-NO-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffff.... ........ + +// CHECK-ORIGINS: 0x{{.*}}: ..00ffff 00000000 ffffffff ffffffff |A . B C| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |D E F G| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |H I J K| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |L M N O| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |P Q R S| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |T U V W| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |X Y Z *| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |* * * A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffffffff ffffffff |A A A A| +// CHECK-ORIGINS: 0x{{.*}}: ffffffff ffffffff ffff.... ........ |A A A .| + +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:14 + +// CHECK-ORIGINS: Origin B (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:18 + +// CHECK-ORIGINS: Origin C (origin_id {{.*}}): +// CHECK-ORIGINS-2: Uninitialized value was stored to memory at +// CHECK-ORIGINS-2: #0 {{.*}} in main{{.*}}msan_print_shadow.cc:48 +// CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'main' +// CHECK-ORIGINS: #0 {{.*}} in main{{.*}}msan_print_shadow.cc:12 + +// CHECK-ORIGINS: Origin D (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:20 + +// ... + +// CHECK-ORIGINS: Origin Z (origin_id {{.*}}): +// CHECK-ORIGINS: Uninitialized value was created by a heap allocation +// CHECK-ORIGINS: #0 {{.*}} in __msan_allocated_memory +// CHECK-ORIGINS: #1 {{.*}} in main{{.*}}msan_print_shadow.cc:42 diff --git a/test/msan/msan_print_shadow2.cc b/test/msan/msan_print_shadow2.cc new file mode 100644 index 0000000000000..343ee9e5c3de3 --- /dev/null +++ b/test/msan/msan_print_shadow2.cc @@ -0,0 +1,49 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NO-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ORIGINS < %t.out + +#include <sanitizer/msan_interface.h> + +int main(void) { + char *p = new char[16]; + __msan_print_shadow(p, 1); + __msan_print_shadow(p+1, 1); + __msan_print_shadow(p+3, 1); + __msan_print_shadow(p+15, 1); + __msan_print_shadow(p, 0); + delete[] p; + int x = 0; + __msan_print_shadow(&x, 3); + return 0; +} + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ff...... ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ff...... ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ..ff.... ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ..ff.... ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 1 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: ......ff ........ ........ ........ |A . . .| +// CHECK-ORIGINS: Origin A (origin_id {{.*}}): + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 0 bytes: + +// CHECK: Shadow map of [0x{{.*}}, 0x{{.*}}), 3 bytes: +// CHECK-NO-ORIGINS: 0x{{.*}}: 000000.. ........ ........ ........ +// CHECK-ORIGINS: 0x{{.*}}: 000000.. ........ ........ ........ |. . . .| diff --git a/test/msan/msan_print_shadow3.cc b/test/msan/msan_print_shadow3.cc new file mode 100644 index 0000000000000..c605ef18886d1 --- /dev/null +++ b/test/msan/msan_print_shadow3.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdint.h> +#include <sanitizer/msan_interface.h> + +int main(void) { + unsigned long long x = 0; // For 8-byte alignment. + uint32_t x_s = 0x12345678U; + __msan_partial_poison(&x, &x_s, sizeof(x_s)); + __msan_print_shadow(&x, sizeof(x_s)); + return 0; +} + +// CHECK: Shadow map of [{{.*}}), 4 bytes: +// CHECK: 0x{{.*}}: 87654321 ........ ........ ........ diff --git a/test/msan/mul_by_const.cc b/test/msan/mul_by_const.cc new file mode 100644 index 0000000000000..a975bb92167ed --- /dev/null +++ b/test/msan/mul_by_const.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> + +struct S { + S(int a0) : a(a0) {} + int a; + int b; +}; + +// Here S is passed to FooRun as a 64-bit integer. +// This triggers an optimization where 10000 * s.a is transformed into +// ((*(uint64_t *)&s) * (10000 * 2**32)) >> 32 +// Test that MSan understands that this kills the uninitialized high half of S +// (i.e. S::b). +void FooRun(S s) { + int64_t x = 10000 * s.a; + __msan_check_mem_is_initialized(&x, sizeof(x)); +} + +int main(void) { + S z(1); + // Take &z to ensure that it is built on stack. + S *volatile p = &z; + FooRun(z); + return 0; +} diff --git a/test/msan/no_sanitize_memory.cc b/test/msan/no_sanitize_memory.cc new file mode 100644 index 0000000000000..c5643816c2812 --- /dev/null +++ b/test/msan/no_sanitize_memory.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 + +// RUN: %clangxx_msan -m64 -O0 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t -DCHECK_IN_F && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t -DCHECK_IN_F && %run %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 0000000000000..4275ebbf78e1f --- /dev/null +++ b/test/msan/no_sanitize_memory_prop.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t >%t.out 2>&1 +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t >%t.out 2>&1 + +// Test that (no_sanitize_memory) functions DO NOT propagate shadow. + +#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); + if (y) + exit(0); + return 0; +} diff --git a/test/msan/obstack.cc b/test/msan/obstack.cc new file mode 100644 index 0000000000000..222f43b839dc9 --- /dev/null +++ b/test/msan/obstack.cc @@ -0,0 +1,37 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DPOSITIVE %s -o %t && not %run %t |& FileCheck %s + +#include <obstack.h> +#include <sanitizer/msan_interface.h> +#include <stdlib.h> + +static void *obstack_chunk_alloc(size_t sz) { + return malloc(sz); +} + +static void obstack_chunk_free(void *p) { + free(p); +} + +int main(void) { + obstack obs; + obstack_init(&obs); + for (size_t sz = 16; sz < 0xFFFF; sz *= 2) { + void *p = obstack_alloc(&obs, sz); + int data[10] = {0}; + obstack_grow(&obs, &data, sizeof(data)); + obstack_blank(&obs, sz); + obstack_grow(&obs, &data, sizeof(data)); + obstack_int_grow(&obs, 13); + p = obstack_finish(&obs); +#ifdef POSITIVE + if (sz == 4096) { + __msan_check_mem_is_initialized(p, sizeof(data)); + __msan_check_mem_is_initialized(p, sizeof(data) + 1); + } + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: #0 0x{{.*}} in main{{.*}}obstack.cc:[[@LINE-30]] +#endif + } + obstack_free(&obs, 0); +} diff --git a/test/msan/param_tls_limit.cc b/test/msan/param_tls_limit.cc new file mode 100644 index 0000000000000..869afc9357737 --- /dev/null +++ b/test/msan/param_tls_limit.cc @@ -0,0 +1,74 @@ +// ParamTLS has limited size. Everything that does not fit is considered fully +// initialized. + +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && %run %t + +#include <sanitizer/msan_interface.h> +#include <assert.h> + +// This test assumes that ParamTLS size is 800 bytes. + +// This test passes poisoned values through function argument list. +// In case of overflow, argument is unpoisoned. +#define OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == -1) +// In case of no overflow, it is still poisoned. +#define NO_OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == 0) + +template<int N> +struct S { + char x[N]; +}; + +void f100(S<100> s) { + NO_OVERFLOW(s); +} + +void f800(S<800> s) { + NO_OVERFLOW(s); +} + +void f801(S<801> s) { + OVERFLOW(s); +} + +void f1000(S<1000> s) { + OVERFLOW(s); +} + +void f_many(int a, double b, S<800> s, int c, double d) { + NO_OVERFLOW(a); + NO_OVERFLOW(b); + OVERFLOW(s); + OVERFLOW(c); + OVERFLOW(d); +} + +// -8 bytes for "int a", aligned by 8 +// -2 to make "int c" a partial fit +void f_many2(int a, S<800 - 8 - 2> s, int c, double d) { + NO_OVERFLOW(a); + NO_OVERFLOW(s); + OVERFLOW(c); + OVERFLOW(d); +} + +int main(void) { + S<100> s100; + S<800> s800; + S<801> s801; + S<1000> s1000; + f100(s100); + f800(s800); + f801(s801); + f1000(s1000); + + int i; + double d; + f_many(i, d, s800, i, d); + + S<800 - 8 - 2> s788; + f_many2(i, s788, i, d); + return 0; +} diff --git a/test/msan/poison_in_free.cc b/test/msan/poison_in_free.cc new file mode 100644 index 0000000000000..16e2124c3d515 --- /dev/null +++ b/test/msan/poison_in_free.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -O0 %s -o %t && not %run %t >%t.out 2>&1 +// FileCheck %s <%t.out +// RUN: %clangxx_msan -O0 %s -o %t && MSAN_OPTIONS=poison_in_free=0 %run %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/print_stats.cc b/test/msan/print_stats.cc new file mode 100644 index 0000000000000..74943835b367d --- /dev/null +++ b/test/msan/print_stats.cc @@ -0,0 +1,45 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g %s -o %t +// RUN: %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1 %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1,atexit=1 %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 %s -o %t +// RUN: not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS %s +// RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS %s + +// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -g -DPOSITIVE=1 -mllvm -msan-keep-going=1 %s -o %t +// RUN: not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-NOSTATS --check-prefix=CHECK-KEEPGOING %s +// RUN: MSAN_OPTIONS=print_stats=1 not %run %t 2>&1 | \ +// RUN: FileCheck --check-prefix=CHECK --check-prefix=CHECK-STATS --check-prefix=CHECK-KEEPGOING %s + +#include <stdio.h> +int main(int argc, char **argv) { + int x; + int *volatile p = &x; + fprintf(stderr, "TEST\n"); +#ifdef POSITIVE + return *p; +#else + return 0; +#endif +} + +// CHECK: TEST + +// CHECK-STATS: Unique heap origins: +// CHECK-STATS: Stack depot allocated bytes: +// CHECK-STATS: Unique origin histories: +// CHECK-STATS: History depot allocated bytes: + +// CHECK-NOSTATS-NOT: Unique heap origins: +// CHECK-NOSTATS-NOT: Stack depot allocated bytes: +// CHECK-NOSTATS-NOT: Unique origin histories: +// CHECK-NOSTATS-NOT: History depot allocated bytes: + +// CHECK-KEEPGOING: MemorySanitizer: 1 warnings reported. diff --git a/test/msan/pthread_getattr_np_deadlock.cc b/test/msan/pthread_getattr_np_deadlock.cc new file mode 100644 index 0000000000000..07f19cb61b6ce --- /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 && %run %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/rand_r.cc b/test/msan/rand_r.cc new file mode 100644 index 0000000000000..d6bdb1deaa686 --- /dev/null +++ b/test/msan/rand_r.cc @@ -0,0 +1,18 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O0 -g -DUNINIT %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +int main(void) { + unsigned seed; +#ifndef UNINIT + seed = 42; +#endif + int v = rand_r(&seed); + // CHECK: MemorySanitizer: use-of-uninitialized-value + // CHECK: in main{{.*}}rand_r.cc:[[@LINE-2]] + if (v) printf(".\n"); + return 0; +} diff --git a/test/msan/readdir64.cc b/test/msan/readdir64.cc new file mode 100644 index 0000000000000..4f00d18387942 --- /dev/null +++ b/test/msan/readdir64.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O1 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O2 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %t + +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O1 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O2 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t +// RUN: %clangxx_msan -m64 -O3 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %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/report-demangling.cc b/test/msan/report-demangling.cc new file mode 100644 index 0000000000000..e6d5c27ec85de --- /dev/null +++ b/test/msan/report-demangling.cc @@ -0,0 +1,19 @@ +// Test that function name is mangled in the "created by an allocation" line, +// and demangled in the single-frame "stack trace" that follows. + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s < %t.out + +__attribute__((noinline)) +int f() { + int x; + int *volatile p = &x; + return *p; +} + +int main(int argc, char **argv) { + return f(); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: Uninitialized value was created by an allocation of 'x' in the stack frame of function '_Z1fv' + // CHECK: #0 {{.*}} in f() {{.*}}report-demangling.cc:[[@LINE-10]] +} diff --git a/test/msan/scandir.cc b/test/msan/scandir.cc new file mode 100644 index 0000000000000..571ba4f603d2c --- /dev/null +++ b/test/msan/scandir.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..e7663dc43a741 --- /dev/null +++ b/test/msan/scandir_null.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O0 -D_FILE_OFFSET_BITS=64 %s -o %t && %run %t %p +// RUN: %clangxx_msan -m64 -O3 %s -o %t && %run %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 0000000000000..e69de29bb2d1d --- /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 0000000000000..e69de29bb2d1d --- /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 0000000000000..e69de29bb2d1d --- /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 0000000000000..89de75ebaaefa --- /dev/null +++ b/test/msan/select.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %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_float_origin.cc b/test/msan/select_float_origin.cc new file mode 100644 index 0000000000000..ca8f3a83b0ed5 --- /dev/null +++ b/test/msan/select_float_origin.cc @@ -0,0 +1,24 @@ +// Regression test for origin propagation in "select i1, float, float". +// https://code.google.com/p/memory-sanitizer/issues/detail?id=78 + +// RUN: %clangxx_msan -O2 -fsanitize-memory-track-origins %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -O2 -fsanitize-memory-track-origins=2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +#include <stdio.h> +#include <sanitizer/msan_interface.h> + +int main() { + volatile bool b = true; + float x, y; + __msan_allocated_memory(&x, sizeof(x)); + __msan_allocated_memory(&y, sizeof(y)); + float z = b ? x : y; + if (z > 0) printf(".\n"); + // CHECK: Uninitialized value was created by a heap allocation + // CHECK: {{#0 0x.* in .*__msan_allocated_memory}} + // CHECK: {{#1 0x.* in main .*select_float_origin.cc:}}[[@LINE-6]] + return 0; +} diff --git a/test/msan/select_origin.cc b/test/msan/select_origin.cc new file mode 100644 index 0000000000000..e832c02e99ced --- /dev/null +++ b/test/msan/select_origin.cc @@ -0,0 +1,22 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_msan -fsanitize-memory-track-origins -O2 %s -o %t && not %run %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 0000000000000..b7007f78da7cd --- /dev/null +++ b/test/msan/setlocale.cc @@ -0,0 +1,13 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..654b9676f4abd --- /dev/null +++ b/test/msan/signal_stress_test.cc @@ -0,0 +1,71 @@ +// RUN: %clangxx_msan -std=c++11 -O0 %s -o %t && %run %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 0000000000000..f2e77cfd64071 --- /dev/null +++ b/test/msan/sigwait.cc @@ -0,0 +1,30 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %run %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 0000000000000..be7a2c009e0e5 --- /dev/null +++ b/test/msan/sigwaitinfo.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -std=c++11 -O0 -g %s -o %t && %run %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 0000000000000..c39c3a8cf6d05 --- /dev/null +++ b/test/msan/stack-origin.cc @@ -0,0 +1,31 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %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/stack-origin2.cc b/test/msan/stack-origin2.cc new file mode 100644 index 0000000000000..0ba57607dadb5 --- /dev/null +++ b/test/msan/stack-origin2.cc @@ -0,0 +1,41 @@ +// Test that on the second entry to a function the origins are still right. + +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out && FileCheck %s --check-prefix=CHECK-ORIGINS < %t.out + +#include <stdlib.h> + +extern "C" +int f(int depth) { + if (depth) return f(depth - 1); + + int x; + int *volatile p = &x; + return *p; +} + +int main(int argc, char **argv) { + return f(1); + // CHECK: WARNING: MemorySanitizer: use-of-uninitialized-value + // CHECK: {{#0 0x.* in main .*stack-origin2.cc:}}[[@LINE-2]] + + // CHECK-ORIGINS: Uninitialized value was created by an allocation of 'x' in the stack frame of function 'f' + // CHECK-ORIGINS: {{#0 0x.* in f .*stack-origin2.cc:}}[[@LINE-14]] + + // CHECK: SUMMARY: MemorySanitizer: use-of-uninitialized-value {{.*stack-origin2.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 0000000000000..d55bf42ef11ee --- /dev/null +++ b/test/msan/strerror_r-non-gnu.c @@ -0,0 +1,18 @@ +// RUN: %clang_msan -std=c99 -O0 -g %s -o %t && %run %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 0000000000000..bb9fe17d41cdb --- /dev/null +++ b/test/msan/strlen_of_shadow.cc @@ -0,0 +1,24 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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/strxfrm.cc b/test/msan/strxfrm.cc new file mode 100644 index 0000000000000..b930a6af69c4d --- /dev/null +++ b/test/msan/strxfrm.cc @@ -0,0 +1,15 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <sanitizer/msan_interface.h> +#include <stdlib.h> +#include <string.h> + +int main(void) { + const char *p = "abcdef"; + char q[10]; + size_t n = strxfrm(q, p, sizeof(q)); + assert(n < sizeof(q)); + __msan_check_mem_is_initialized(q, n + 1); + 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 0000000000000..b6d344a613403 --- /dev/null +++ b/test/msan/sync_lock_set_and_test.cc @@ -0,0 +1,7 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..47e991e8c16b7 --- /dev/null +++ b/test/msan/textdomain.cc @@ -0,0 +1,12 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %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 0000000000000..a042f548dc0c6 --- /dev/null +++ b/test/msan/times.cc @@ -0,0 +1,20 @@ +// RUN: %clangxx_msan -m64 -O0 -g %s -o %t && %run %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 0000000000000..e024a5a8d13f5 --- /dev/null +++ b/test/msan/tls_reuse.cc @@ -0,0 +1,26 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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/tsearch.cc b/test/msan/tsearch.cc new file mode 100644 index 0000000000000..653dc60371cd5 --- /dev/null +++ b/test/msan/tsearch.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_msan -O0 -g %s -o %t && %run %t + +#include <assert.h> +#include <search.h> +#include <stdlib.h> + +int compare(const void *pa, const void *pb) { + int a = *(const int *)pa; + int b = *(const int *)pb; + if (a < b) + return -1; + else if (a > b) + return 1; + else + return 0; +} + +void myfreenode(void *p) { + delete (int *)p; +} + +int main(void) { + void *root = NULL; + for (int i = 0; i < 5; ++i) { + int *p = new int(i); + void *q = tsearch(p, &root, compare); + if (q == NULL) + exit(1); + if (*(int **)q != p) + delete p; + } + + tdestroy(root, myfreenode); + + return 0; +} diff --git a/test/msan/tzset.cc b/test/msan/tzset.cc new file mode 100644 index 0000000000000..ed61d7bcc4497 --- /dev/null +++ b/test/msan/tzset.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %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 0000000000000..e5618efbde24a --- /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 %run %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 %run %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/unpoison_string.cc b/test/msan/unpoison_string.cc new file mode 100644 index 0000000000000..ac947b9cf6fde --- /dev/null +++ b/test/msan/unpoison_string.cc @@ -0,0 +1,16 @@ +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t +// RUN: %run %t +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O3 %s -o %t +// RUN: %run %t + +#include <assert.h> +#include <string.h> +#include <sanitizer/msan_interface.h> + +int main(int argc, char **argv) { + char s[20] = "string"; + __msan_poison(s, sizeof(s)); + __msan_unpoison_string(s); + assert(__msan_test_shadow(s, sizeof(s)) == strlen("string") + 1); + return 0; +} diff --git a/test/msan/use-after-free.cc b/test/msan/use-after-free.cc new file mode 100644 index 0000000000000..5b408c5364959 --- /dev/null +++ b/test/msan/use-after-free.cc @@ -0,0 +1,34 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O1 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O2 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out +// RUN: %clangxx_msan -m64 -O3 %s -o %t && not %run %t >%t.out 2>&1 +// RUN: FileCheck %s < %t.out + +// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && not %run %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 %run %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 %run %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 %run %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 0000000000000..bd9b6a8b80002 --- /dev/null +++ b/test/msan/vector_cvt.cc @@ -0,0 +1,23 @@ +// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t +// RUN: %clangxx_msan -DPOSITIVE -m64 -O0 %s -o %t && not %run %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-3]] +} + +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 0000000000000..e8d55423293c4 --- /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; +} + |