diff options
Diffstat (limited to 'test/tsan')
35 files changed, 674 insertions, 48 deletions
diff --git a/test/tsan/CMakeLists.txt b/test/tsan/CMakeLists.txt index 5a9542fd76fc..2996c1d80fbd 100644 --- a/test/tsan/CMakeLists.txt +++ b/test/tsan/CMakeLists.txt @@ -1,5 +1,7 @@ set(TSAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) -list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck) +if(NOT ${LLVM_NATIVE_ARCH} STREQUAL "Mips") + list(APPEND TSAN_TEST_DEPS GotsanRuntimeCheck) +endif() if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TSAN_TEST_DEPS tsan) endif() diff --git a/test/tsan/cond_cancel.c b/test/tsan/cond_cancel.c index e744570b12fc..ddfb745174f6 100644 --- a/test/tsan/cond_cancel.c +++ b/test/tsan/cond_cancel.c @@ -8,9 +8,14 @@ pthread_mutex_t m; pthread_cond_t c; int x; +static void my_cleanup(void *arg) { + printf("my_cleanup\n"); + pthread_mutex_unlock((pthread_mutex_t*)arg); +} + void *thr1(void *p) { pthread_mutex_lock(&m); - pthread_cleanup_push((void(*)(void *arg))pthread_mutex_unlock, &m); + pthread_cleanup_push(my_cleanup, &m); barrier_wait(&barrier); while (x == 0) pthread_cond_wait(&c, &m); diff --git a/test/tsan/cond_destruction.cc b/test/tsan/cond_destruction.cc new file mode 100644 index 000000000000..f56b30c4f0a2 --- /dev/null +++ b/test/tsan/cond_destruction.cc @@ -0,0 +1,53 @@ +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s +// RUN: %run %t arg 2>&1 | FileCheck %s +// RUN: %run %t arg arg 2>&1 | FileCheck %s +#include "test.h" + +// Test for destruction of pthread_cond_t. +// POSIX states that it is safe to destroy a condition variable upon which no +// threads are currently blocked. That is, it is not necessary to wait untill +// other threads return from pthread_cond_wait, they just need to be unblocked. + +pthread_mutex_t m; +pthread_cond_t c; +bool done1, done2; + +void *thr(void *p) { + pthread_mutex_lock(&m); + done1 = true; + pthread_cond_signal(&c); + while (!done2) + pthread_cond_wait(&c, &m); + pthread_mutex_unlock(&m); + return 0; +} + +int main(int argc, char **argv) { + pthread_t th; + pthread_mutex_init(&m, 0); + pthread_cond_init(&c, 0); + pthread_create(&th, 0, thr, 0); + pthread_mutex_lock(&m); + while (!done1) + pthread_cond_wait(&c, &m); + done2 = true; + // Any of these sequences is legal. + if (argc == 1) { + pthread_cond_signal(&c); + pthread_mutex_unlock(&m); + pthread_cond_destroy(&c); + } else if (argc == 2) { + pthread_mutex_unlock(&m); + pthread_cond_signal(&c); + pthread_cond_destroy(&c); + } else { + pthread_cond_signal(&c); + pthread_cond_destroy(&c); + pthread_mutex_unlock(&m); + } + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK-NOT: ThreadSanitizer: data race diff --git a/test/tsan/cond_race.cc b/test/tsan/cond_race.cc index 52654f16e85c..4daf37f85414 100644 --- a/test/tsan/cond_race.cc +++ b/test/tsan/cond_race.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s // CHECK-NOT: unlock of unlocked mutex // CHECK: ThreadSanitizer: data race // CHECK: pthread_cond_signal diff --git a/test/tsan/deadlock_detector_stress_test.cc b/test/tsan/deadlock_detector_stress_test.cc index e02a9123f5af..c77ffe555ce5 100644 --- a/test/tsan/deadlock_detector_stress_test.cc +++ b/test/tsan/deadlock_detector_stress_test.cc @@ -1,6 +1,6 @@ // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex // RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND -// TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND +// RUN: TSAN_OPTIONS="detect_deadlocks=1 second_deadlock_stack=1" %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock // RUN: TSAN_OPTIONS=detect_deadlocks=1 %deflake %run %t | FileCheck %s // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock diff --git a/test/tsan/dl_iterate_phdr.cc b/test/tsan/dl_iterate_phdr.cc new file mode 100644 index 000000000000..b230a920ac4f --- /dev/null +++ b/test/tsan/dl_iterate_phdr.cc @@ -0,0 +1,56 @@ +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script. + +#ifdef BUILD_SO + +#include "test.h" + +int exported_var = 0; + +#else // BUILD_SO + +#include "test.h" +#include <dlfcn.h> +#include <link.h> +#include <string.h> +#include <string> + +static int callback(struct dl_phdr_info *info, size_t size, void *data) { + if (info->dlpi_name[0] == '\0') + info->dlpi_name = "/proc/self/exe"; + return !strcmp(info->dlpi_name, "non existent module"); +} + +void *thread(void *unused) { + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + dl_iterate_phdr(callback, 0); + } + return 0; +} + +int main(int argc, char *argv[]) { + barrier_init(&barrier, 2); + std::string path = std::string(argv[0]) + std::string("-so.so"); + pthread_t th; + pthread_create(&th, 0, thread, 0); + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen: %s\n", dlerror()); + return 1; + } + dlclose(lib); + } + pthread_join(th, 0); + printf("DONE\n"); + return 0; +} + +#endif // BUILD_SO + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/fd_dup_norace2.cc b/test/tsan/fd_dup_norace2.cc new file mode 100644 index 000000000000..662c686f33a8 --- /dev/null +++ b/test/tsan/fd_dup_norace2.cc @@ -0,0 +1,60 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +// dup2(oldfd, newfd) races with read(newfd). +// This is not reported as race because: +// 1. Some software dups a closed pipe in place of a socket before closing +// the socket (to prevent races actually). +// 2. Some daemons dup /dev/null in place of stdin/stdout. + +int fd; + +void *Thread(void *x) { + char buf; + int n = read(fd, &buf, 1); + if (n != 1) { + // This read can "legitimately" fail regadless of the fact that glibc claims + // that "there is no instant in the middle of calling dup2 at which new is + // closed and not yet a duplicate of old". Strace of the failing runs + // looks as follows: + // + // [pid 122196] open("/dev/urandom", O_RDONLY) = 3 + // [pid 122196] open("/dev/urandom", O_RDONLY) = 4 + // Process 122382 attached + // [pid 122382] read(3, <unfinished ...> + // [pid 122196] dup2(4, 3 <unfinished ...> + // [pid 122382] <... read resumed> 0x7fcd139960b7, 1) = -1 EBADF (Bad file descriptor) + // [pid 122196] <... dup2 resumed> ) = 3 + // read failed: n=-1 errno=9 + // + // The failing read does not interfere with what this test tests, + // so we just ignore the failure. + // + // exit(printf("read failed: n=%d errno=%d\n", n, errno)); + } + return 0; +} + +int main() { + fd = open("/dev/urandom", O_RDONLY); + int fd2 = open("/dev/urandom", O_RDONLY); + if (fd == -1 || fd2 == -1) + exit(printf("open failed\n")); + pthread_t th; + pthread_create(&th, 0, Thread, 0); + if (dup2(fd2, fd) == -1) + exit(printf("dup2 failed\n")); + pthread_join(th, 0); + if (close(fd) == -1) + exit(printf("close failed\n")); + if (close(fd2) == -1) + exit(printf("close failed\n")); + printf("DONE\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/fd_dup_race.cc b/test/tsan/fd_dup_race.cc new file mode 100644 index 000000000000..a1aee5500753 --- /dev/null +++ b/test/tsan/fd_dup_race.cc @@ -0,0 +1,33 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +// dup2(oldfd, newfd) races with close(newfd). + +int fd; + +void *Thread(void *x) { + barrier_wait(&barrier); + if (close(fd) == -1) + exit(printf("close failed\n")); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + fd = open("/dev/random", O_RDONLY); + int fd2 = open("/dev/random", O_RDONLY); + if (fd == -1 || fd2 == -1) + exit(printf("open failed\n")); + pthread_t th; + pthread_create(&th, 0, Thread, 0); + if (dup2(fd2, fd) == -1) + exit(printf("dup2 failed\n")); + barrier_wait(&barrier); + pthread_join(th, 0); + printf("DONE\n"); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/heap_race.cc b/test/tsan/heap_race.cc index c3da68f42658..0201ea9a2e7f 100644 --- a/test/tsan/heap_race.cc +++ b/test/tsan/heap_race.cc @@ -1,17 +1,21 @@ // RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "test.h" #include <pthread.h> #include <stdio.h> #include <stddef.h> void *Thread(void *a) { ((int*)a)[0]++; + barrier_wait(&barrier); return NULL; } int main() { + barrier_init(&barrier, 2); int *p = new int(42); pthread_t t; pthread_create(&t, NULL, Thread, p); + barrier_wait(&barrier); p[0]++; pthread_join(t, NULL); delete p; diff --git a/test/tsan/ignore_free.cc b/test/tsan/ignore_free.cc index bb6c6ee14364..4e678952c7aa 100644 --- a/test/tsan/ignore_free.cc +++ b/test/tsan/ignore_free.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s #include "test.h" extern "C" { diff --git a/test/tsan/ignore_malloc.cc b/test/tsan/ignore_malloc.cc index 1f633f062d0e..100b4e5dc808 100644 --- a/test/tsan/ignore_malloc.cc +++ b/test/tsan/ignore_malloc.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s #include "test.h" extern "C" { diff --git a/test/tsan/java.h b/test/tsan/java.h index 35fdbc1e7bb1..565a7a7fdabf 100644 --- a/test/tsan/java.h +++ b/test/tsan/java.h @@ -18,4 +18,9 @@ int __tsan_java_mutex_unlock_rec(jptr addr); int __tsan_java_acquire(jptr addr); int __tsan_java_release(jptr addr); int __tsan_java_release_store(jptr addr); + +void __tsan_read1_pc(jptr addr, jptr pc); +void __tsan_write1_pc(jptr addr, jptr pc); } + +const jptr kExternalPCBit = 1ULL << 60; diff --git a/test/tsan/java_heap_init.cc b/test/tsan/java_heap_init.cc new file mode 100644 index 000000000000..bb7357c25b42 --- /dev/null +++ b/test/tsan/java_heap_init.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "java.h" +#include <errno.h> +#include <sys/mman.h> + +int main() { + // Test that munmap interceptor resets meta shadow for the memory range. + // Previously __tsan_java_move failed because it encountered non-zero meta + // shadow for the destination. + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)mmap(0, kHeapSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (jheap == (jptr)MAP_FAILED) + return printf("mmap failed with %d\n", errno); + __atomic_store_n((int*)jheap, 1, __ATOMIC_RELEASE); + munmap((void*)jheap, kHeapSize); + jheap = (jptr)mmap((void*)jheap, kHeapSize, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + if (jheap == (jptr)MAP_FAILED) + return printf("second mmap failed with %d\n", errno); + __tsan_java_init(jheap, kHeapSize); + __tsan_java_move(jheap + 16, jheap, 16); + printf("DONE\n"); + return __tsan_java_fini(); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE diff --git a/test/tsan/java_race.cc b/test/tsan/java_race.cc index ede058e85d8a..140a2a3c19d3 100644 --- a/test/tsan/java_race.cc +++ b/test/tsan/java_race.cc @@ -2,11 +2,13 @@ #include "java.h" void *Thread(void *p) { + barrier_wait(&barrier); *(int*)p = 42; return 0; } int main() { + barrier_init(&barrier, 2); int const kHeapSize = 1024 * 1024; jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; __tsan_java_init(jheap, kHeapSize); @@ -15,6 +17,7 @@ int main() { pthread_t th; pthread_create(&th, 0, Thread, (void*)jheap); *(int*)jheap = 43; + barrier_wait(&barrier); pthread_join(th, 0); __tsan_java_free(jheap, kBlockSize); fprintf(stderr, "DONE\n"); diff --git a/test/tsan/java_race_pc.cc b/test/tsan/java_race_pc.cc new file mode 100644 index 000000000000..015a0b1f43c6 --- /dev/null +++ b/test/tsan/java_race_pc.cc @@ -0,0 +1,36 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" + +void foobar() { +} + +void barbaz() { +} + +void *Thread(void *p) { + barrier_wait(&barrier); + __tsan_read1_pc((jptr)p, (jptr)foobar + 1); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + __tsan_write1_pc((jptr)jheap, (jptr)barbaz + 1); + barrier_wait(&barrier); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 foobar +// CHECK: #0 barbaz +// CHECK: DONE diff --git a/test/tsan/java_symbolization.cc b/test/tsan/java_symbolization.cc new file mode 100644 index 000000000000..aa5ec0c37558 --- /dev/null +++ b/test/tsan/java_symbolization.cc @@ -0,0 +1,44 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "java.h" +#include <memory.h> + +extern "C" bool __tsan_symbolize_external(jptr pc, + char *func_buf, jptr func_siz, + char *file_buf, jptr file_siz, + int *line, int *col) { + if (pc == (1234 | kExternalPCBit)) { + memcpy(func_buf, "MyFunc", sizeof("MyFunc")); + memcpy(file_buf, "MyFile.java", sizeof("MyFile.java")); + *line = 1234; + *col = 56; + return true; + } + return false; +} + +void *Thread(void *p) { + barrier_wait(&barrier); + __tsan_write1_pc((jptr)p, 1234 | kExternalPCBit); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + int const kHeapSize = 1024 * 1024; + jptr jheap = (jptr)malloc(kHeapSize + 8) + 8; + __tsan_java_init(jheap, kHeapSize); + const int kBlockSize = 16; + __tsan_java_alloc(jheap, kBlockSize); + pthread_t th; + pthread_create(&th, 0, Thread, (void*)jheap); + __tsan_write1_pc((jptr)jheap, 1234 | kExternalPCBit); + barrier_wait(&barrier); + pthread_join(th, 0); + __tsan_java_free(jheap, kBlockSize); + fprintf(stderr, "DONE\n"); + return __tsan_java_fini(); +} + +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: #0 MyFunc MyFile.java:1234:56 +// CHECK: DONE diff --git a/test/tsan/large_malloc_meta.cc b/test/tsan/large_malloc_meta.cc new file mode 100644 index 000000000000..e83004824a3a --- /dev/null +++ b/test/tsan/large_malloc_meta.cc @@ -0,0 +1,28 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/mman.h> + +// Test for previously unbounded memory consumption for large mallocs. +// Code allocates a large memory block (that is handled by LargeMmapAllocator), +// and forces allocation of meta shadow for the block. Then freed the block. +// But meta shadow was not unmapped. Then code occupies the virtual memory +// range of the block with something else (that does not need meta shadow). +// And repeats. As the result meta shadow growed infinitely. +// This program used to consume >2GB. Now it consumes <50MB. + +int main() { + for (int i = 0; i < 1000; i++) { + const int kSize = 1 << 20; + const int kPageSize = 4 << 10; + volatile int *p = new int[kSize]; + for (int j = 0; j < kSize; j += kPageSize / sizeof(*p)) + __atomic_store_n(&p[i], 1, __ATOMIC_RELEASE); + delete[] p; + mmap(0, kSize * sizeof(*p) + kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, + -1, 0); + } + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/longjmp.cc b/test/tsan/longjmp.cc index d7371c5e4069..d642067391fd 100644 --- a/test/tsan/longjmp.cc +++ b/test/tsan/longjmp.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp2.cc b/test/tsan/longjmp2.cc index 546019b2d11a..eee423dc5fbe 100644 --- a/test/tsan/longjmp2.cc +++ b/test/tsan/longjmp2.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <stdio.h> #include <stdlib.h> #include <setjmp.h> diff --git a/test/tsan/longjmp3.cc b/test/tsan/longjmp3.cc index 71d964dbbed9..79965c4193d3 100644 --- a/test/tsan/longjmp3.cc +++ b/test/tsan/longjmp3.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/longjmp4.cc b/test/tsan/longjmp4.cc index 15330f5d83c8..c8583997331e 100644 --- a/test/tsan/longjmp4.cc +++ b/test/tsan/longjmp4.cc @@ -1,4 +1,8 @@ // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s + +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <pthread.h> #include <stdio.h> #include <stdlib.h> diff --git a/test/tsan/malloc_stack.cc b/test/tsan/malloc_stack.cc index ba1d62bcd9e7..f0c6f9354a5f 100644 --- a/test/tsan/malloc_stack.cc +++ b/test/tsan/malloc_stack.cc @@ -1,4 +1,4 @@ -// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s #include "test.h" _Atomic(int*) p; diff --git a/test/tsan/mmap_large.cc b/test/tsan/mmap_large.cc index 4ae4c0863501..098530475df5 100644 --- a/test/tsan/mmap_large.cc +++ b/test/tsan/mmap_large.cc @@ -4,6 +4,13 @@ #include <errno.h> #include <sys/mman.h> +#if defined(__FreeBSD__) +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 +#endif + int main() { #ifdef __x86_64__ const size_t kLog2Size = 39; diff --git a/test/tsan/mmap_stress.cc b/test/tsan/mmap_stress.cc new file mode 100644 index 000000000000..5e3904adf90b --- /dev/null +++ b/test/tsan/mmap_stress.cc @@ -0,0 +1,47 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <errno.h> +#include <sys/mman.h> + +void *SubWorker(void *arg) { + (void)arg; + const int kMmapSize = 65536; + for (int i = 0; i < 500; i++) { + int *ptr = (int*)mmap(0, kMmapSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, -1, 0); + *ptr = 42; + munmap(ptr, kMmapSize); + } + return 0; +} + +void *Worker1(void *arg) { + (void)arg; + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, SubWorker, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + return 0; +} + +void *Worker(void *arg) { + (void)arg; + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, Worker1, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + return 0; +} + +int main() { + pthread_t th[4]; + for (int i = 0; i < 4; i++) + pthread_create(&th[i], 0, Worker, 0); + for (int i = 0; i < 4; i++) + pthread_join(th[i], 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK: DONE diff --git a/test/tsan/mop1.c b/test/tsan/mop1.c new file mode 100644 index 000000000000..e61c5b8caac9 --- /dev/null +++ b/test/tsan/mop1.c @@ -0,0 +1,40 @@ +// RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "test.h" + +// We want to establish the following sequence of accesses to X: +// - main thread writes X +// - thread2 reads X, this read happens-before the write in main thread +// - thread1 reads X, this read is concurrent with the write in main thread +// Write in main thread and read in thread1 should be detected as a race. +// Previously tsan replaced write by main thread with read by thread1, +// as the result the race was not detected. + +volatile long X, Y, Z; + +void *Thread1(void *x) { + barrier_wait(&barrier); + barrier_wait(&barrier); + Y = X; + return NULL; +} + +void *Thread2(void *x) { + Z = X; + barrier_wait(&barrier); + return NULL; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t[2]; + pthread_create(&t[0], 0, Thread1, 0); + X = 42; + barrier_wait(&barrier); + pthread_create(&t[1], 0, Thread2, 0); + pthread_join(t[0], 0); + pthread_join(t[1], 0); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: data race + diff --git a/test/tsan/race_top_suppression.cc b/test/tsan/race_top_suppression.cc new file mode 100644 index 000000000000..7d42dbf3b4bf --- /dev/null +++ b/test/tsan/race_top_suppression.cc @@ -0,0 +1,29 @@ +// RUN: echo "race_top:TopFunction" > %t.supp +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %run %t 2>&1 | FileCheck %s +// RUN: rm %t.supp +#include "test.h" + +int Global; + +void TopFunction(int *p) { + *p = 1; +} + +void *Thread(void *x) { + barrier_wait(&barrier); + TopFunction(&Global); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + Global--; + barrier_wait(&barrier); + pthread_join(t, 0); + fprintf(stderr, "DONE\n"); +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/race_top_suppression1.cc b/test/tsan/race_top_suppression1.cc new file mode 100644 index 000000000000..881e661ba789 --- /dev/null +++ b/test/tsan/race_top_suppression1.cc @@ -0,0 +1,32 @@ +// RUN: echo "race_top:TopFunction" > %t.supp +// RUN: %clangxx_tsan -O1 %s -o %t +// RUN: TSAN_OPTIONS="$TSAN_OPTIONS suppressions='%t.supp'" %deflake %run %t 2>&1 | FileCheck %s +// RUN: rm %t.supp +#include "test.h" + +int Global; + +void AnotherFunction(int *p) { + *p = 1; +} + +void TopFunction(int *p) { + AnotherFunction(p); +} + +void *Thread(void *x) { + barrier_wait(&barrier); + TopFunction(&Global); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + pthread_create(&t, 0, Thread, 0); + Global--; + barrier_wait(&barrier); + pthread_join(t, 0); +} + +// CHECK: WARNING: ThreadSanitizer: data race diff --git a/test/tsan/setuid.c b/test/tsan/setuid.c new file mode 100644 index 000000000000..bc9c8ca3abaa --- /dev/null +++ b/test/tsan/setuid.c @@ -0,0 +1,26 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <unistd.h> + +// Setuid call used to hang because the background tsan thread did not handle +// SIGSETXID signal. Note that we don't care whether setuid call succeeds +// or not. + +static void *thread(void *arg) { + (void)arg; + sleep(1); + return 0; +} + +int main() { + // Create another thread just for completeness of the picture. + pthread_t th; + pthread_create(&th, 0, thread, 0); + setuid(0); + pthread_join(th, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/setuid2.c b/test/tsan/setuid2.c new file mode 100644 index 000000000000..67a6fd14dbcb --- /dev/null +++ b/test/tsan/setuid2.c @@ -0,0 +1,21 @@ +// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <sys/types.h> +#include <unistd.h> +#include <time.h> + +// Test that setuid call works in presence of stoptheworld. + +int main() { + struct timespec tp0, tp1; + clock_gettime(CLOCK_MONOTONIC, &tp0); + clock_gettime(CLOCK_MONOTONIC, &tp1); + while (tp1.tv_sec - tp0.tv_sec < 3) { + clock_gettime(CLOCK_MONOTONIC, &tp1); + setuid(0); + } + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: DONE diff --git a/test/tsan/signal_cond.cc b/test/tsan/signal_cond.cc new file mode 100644 index 000000000000..f5eae745d407 --- /dev/null +++ b/test/tsan/signal_cond.cc @@ -0,0 +1,51 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <semaphore.h> + +// Test that signals can be delivered to blocked pthread_cond_wait. +// https://code.google.com/p/thread-sanitizer/issues/detail?id=91 + +int g_thread_run = 1; +pthread_mutex_t mutex; +pthread_cond_t cond; +sem_t sem; + +void sig_handler(int sig) { + (void)sig; + write(1, "SIGNAL\n", sizeof("SIGNAL\n") - 1); + sem_post(&sem); +} + +void* my_thread(void* arg) { + pthread_mutex_lock(&mutex); + while (g_thread_run) + pthread_cond_wait(&cond, &mutex); + pthread_mutex_unlock(&mutex); + return 0; +} + +int main() { + sem_init(&sem, 0, 0); + signal(SIGUSR1, &sig_handler); + pthread_t thr; + pthread_create(&thr, 0, &my_thread, 0); + // wait for thread to get inside pthread_cond_wait + // (can't use barrier_wait for that) + sleep(1); + pthread_kill(thr, SIGUSR1); + while (sem_wait(&sem) == -1 && errno == EINTR) { + } + pthread_mutex_lock(&mutex); + g_thread_run = 0; + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); + pthread_join(thr, 0); + fprintf(stderr, "DONE\n"); + return 0; +} + +// CHECK: SIGNAL +// CHECK: DONE diff --git a/test/tsan/signal_longjmp.cc b/test/tsan/signal_longjmp.cc index 84b0682dcbaf..2525c898887b 100644 --- a/test/tsan/signal_longjmp.cc +++ b/test/tsan/signal_longjmp.cc @@ -3,6 +3,9 @@ // Test case for longjumping out of signal handler: // https://code.google.com/p/thread-sanitizer/issues/detail?id=75 +// Longjmp assembly has not been implemented for mips64 yet +// XFAIL: mips64 + #include <setjmp.h> #include <signal.h> #include <stdlib.h> diff --git a/test/tsan/signal_recursive.cc b/test/tsan/signal_recursive.cc index 825338de9fba..67fc9c0ec9a3 100644 --- a/test/tsan/signal_recursive.cc +++ b/test/tsan/signal_recursive.cc @@ -3,6 +3,8 @@ // Test case for recursive signal handlers, adopted from: // https://code.google.com/p/thread-sanitizer/issues/detail?id=71 +// REQUIRES: disabled + #include "test.h" #include <semaphore.h> #include <signal.h> diff --git a/test/tsan/signal_segv_handler.cc b/test/tsan/signal_segv_handler.cc deleted file mode 100644 index 2d806eef6764..000000000000 --- a/test/tsan/signal_segv_handler.cc +++ /dev/null @@ -1,39 +0,0 @@ -// RUN: %clang_tsan -O1 %s -o %t && TSAN_OPTIONS="flush_memory_ms=1 memory_limit_mb=1" %run %t 2>&1 | FileCheck %s - -// JVM uses SEGV to preempt threads. All threads do a load from a known address -// periodically. When runtime needs to preempt threads, it unmaps the page. -// Threads start triggering SEGV one by one. The signal handler blocks -// threads while runtime does its thing. Then runtime maps the page again -// and resumes the threads. -// Previously this pattern conflicted with stop-the-world machinery, -// because it briefly reset SEGV handler to SIG_DFL. -// As the consequence JVM just silently died. - -// This test sets memory flushing rate to maximum, then does series of -// "benign" SEGVs that are handled by signal handler, and ensures that -// the process survive. - -#include "test.h" -#include <signal.h> -#include <sys/mman.h> - -void *guard; - -void handler(int signo, siginfo_t *info, void *uctx) { - mprotect(guard, 4096, PROT_READ | PROT_WRITE); -} - -int main() { - struct sigaction a; - a.sa_sigaction = handler; - a.sa_flags = SA_SIGINFO; - sigaction(SIGSEGV, &a, 0); - guard = mmap(0, 4096, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0); - for (int i = 0; i < 1000000; i++) { - mprotect(guard, 4096, PROT_NONE); - *(int*)guard = 1; - } - fprintf(stderr, "DONE\n"); -} - -// CHECK: DONE diff --git a/test/tsan/test.h b/test/tsan/test.h index bb861b07745e..4e877f6d8dfd 100644 --- a/test/tsan/test.h +++ b/test/tsan/test.h @@ -11,10 +11,16 @@ __typeof(pthread_barrier_wait) *barrier_wait; void barrier_init(pthread_barrier_t *barrier, unsigned count) { +#if defined(__FreeBSD__) + static const char libpthread_name[] = "libpthread.so"; +#else + static const char libpthread_name[] = "libpthread.so.0"; +#endif + if (barrier_wait == 0) { - void *h = dlopen("libpthread.so.0", RTLD_LAZY); + void *h = dlopen(libpthread_name, RTLD_LAZY); if (h == 0) { - fprintf(stderr, "failed to dlopen libpthread.so.0, exiting\n"); + fprintf(stderr, "failed to dlopen %s, exiting\n", libpthread_name); exit(1); } barrier_wait = (__typeof(barrier_wait))dlsym(h, "pthread_barrier_wait"); diff --git a/test/tsan/thread_detach2.c b/test/tsan/thread_detach2.c new file mode 100644 index 000000000000..8133980ba5a1 --- /dev/null +++ b/test/tsan/thread_detach2.c @@ -0,0 +1,28 @@ +// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s +#include "test.h" + +// Test for https://llvm.org/bugs/show_bug.cgi?id=23235 +// The bug was that synchronization between thread creation and thread start +// is not established if pthread_create is followed by pthread_detach. + +int x; + +void *Thread(void *a) { + x = 42; + barrier_wait(&barrier); + return 0; +} + +int main() { + barrier_init(&barrier, 2); + pthread_t t; + x = 43; + pthread_create(&t, 0, Thread, 0); + pthread_detach(t); + barrier_wait(&barrier); + printf("PASS\n"); + return 0; +} + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: PASS |