| // 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 -O3 %s -o %t |
| // RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t 2>&1 | 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 |
| |
| // Flaky on PPC64. |
| // UNSUPPORTED: powerpc64-target-arch |
| // UNSUPPORTED: powerpc64le-target-arch |
| |
| // Sometimes hangs |
| // UNSUPPORTED: target={{.*netbsd.*}} |
| |
| #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 |