/ test / msan / fork.cc
fork.cc
  1  // Test that chained origins are fork-safe.
  2  // Run a number of threads that create new chained origins, then fork
  3  // and verify that origin reads do not deadlock in the child process.
  4  
  5  // RUN: %clangxx_msan -std=c++11 -fsanitize-memory-track-origins=2 -g -O3 %s -o %t
  6  // RUN: MSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t |& FileCheck %s
  7  
  8  // Fun fact: if test output is redirected to a file (as opposed to
  9  // being piped directly to FileCheck), we may lose some "done"s due to
 10  // a kernel bug:
 11  // https://lkml.org/lkml/2014/2/17/324
 12  
 13  
 14  #include <pthread.h>
 15  #include <unistd.h>
 16  #include <stdio.h>
 17  #include <stdlib.h>
 18  #include <sys/types.h>
 19  #include <sys/wait.h>
 20  #include <sys/time.h>
 21  #include <signal.h>
 22  #include <errno.h>
 23  
 24  #include <sanitizer/msan_interface.h>
 25  
 26  int done;
 27  
 28  void copy_uninit_thread2() {
 29    volatile int x;
 30    volatile int v;
 31    while (true) {
 32      v = x;
 33      x = v;
 34      if (__atomic_load_n(&done, __ATOMIC_RELAXED))
 35        return;
 36    }
 37  }
 38  
 39  void copy_uninit_thread1(int level) {
 40    if (!level)
 41      copy_uninit_thread2();
 42    else
 43      copy_uninit_thread1(level - 1);
 44  }
 45  
 46  void *copy_uninit_thread(void *id) {
 47    copy_uninit_thread1((long)id);
 48    return 0;
 49  }
 50  
 51  // Run through stackdepot in the child process.
 52  // If any of the hash table cells are locked, this may deadlock.
 53  void child() {
 54    volatile int x;
 55    volatile int v;
 56    for (int i = 0; i < 10000; ++i) {
 57      v = x;
 58      x = v;
 59    }
 60    write(2, "done\n", 5);
 61  }
 62  
 63  void test() {
 64    const int kThreads = 10;
 65    pthread_t t[kThreads];
 66    for (int i = 0; i < kThreads; ++i)
 67      pthread_create(&t[i], NULL, copy_uninit_thread, (void*)(long)i);
 68    usleep(100000);
 69    pid_t pid = fork();
 70    if (pid) {
 71      // parent
 72      __atomic_store_n(&done, 1, __ATOMIC_RELAXED);
 73      pid_t p;
 74      while ((p = wait(NULL)) == -1) {  }
 75    } else {
 76      // child
 77      child();
 78    }
 79  }
 80  
 81  int main() {
 82    const int kChildren = 20;
 83    for (int i = 0; i < kChildren; ++i) {
 84      pid_t pid = fork();
 85      if (pid) {
 86        // parent
 87      } else {
 88        test();
 89        exit(0);
 90      }
 91    }
 92    
 93    for (int i = 0; i < kChildren; ++i) {
 94      pid_t p;
 95      while ((p = wait(NULL)) == -1) {  }
 96    }
 97  
 98    return 0;
 99  }
100  
101  // Expect 20 (== kChildren) "done" messages.
102  // CHECK: done
103  // CHECK: done
104  // CHECK: done
105  // CHECK: done
106  // CHECK: done
107  // CHECK: done
108  // CHECK: done
109  // CHECK: done
110  // CHECK: done
111  // CHECK: done
112  // CHECK: done
113  // CHECK: done
114  // CHECK: done
115  // CHECK: done
116  // CHECK: done
117  // CHECK: done
118  // CHECK: done
119  // CHECK: done
120  // CHECK: done
121  // CHECK: done