|  | // 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 |