| 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_dfsan %s -o %t |
| 6 | // RUN: %run %t 2>&1 | FileCheck %s |
| 7 | // |
| 8 | // RUN: %clangxx_dfsan -mllvm -dfsan-track-origins=1 %s -o %t |
| 9 | // RUN: env DFSAN_OPTIONS=store_context_size=1000,origin_history_size=0,origin_history_per_stack_limit=0 %run %t 2>&1 | FileCheck %s |
| 10 | |
| 11 | #include <assert.h> |
| 12 | #include <errno.h> |
| 13 | #include <pthread.h> |
| 14 | #include <signal.h> |
| 15 | #include <stdio.h> |
| 16 | #include <stdlib.h> |
| 17 | #include <sys/time.h> |
| 18 | #include <sys/types.h> |
| 19 | #include <sys/wait.h> |
| 20 | #include <unistd.h> |
| 21 | |
| 22 | #include <sanitizer/dfsan_interface.h> |
| 23 | |
| 24 | int done; |
| 25 | |
| 26 | void copy_labels_thread2() { |
| 27 | volatile int x = 0; |
| 28 | volatile int v = 0; |
| 29 | dfsan_set_label(label: 8, addr: (void *)&x, size: sizeof(x)); |
| 30 | while (true) { |
| 31 | v = x; |
| 32 | x = v; |
| 33 | if (__atomic_load_n(&done, __ATOMIC_RELAXED)) |
| 34 | return; |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | void copy_labels_thread1(int level) { |
| 39 | if (!level) |
| 40 | copy_labels_thread2(); |
| 41 | else |
| 42 | copy_labels_thread1(level: level - 1); |
| 43 | } |
| 44 | |
| 45 | void *copy_labels_thread(void *id) { |
| 46 | copy_labels_thread1(level: (long)id); |
| 47 | return 0; |
| 48 | } |
| 49 | |
| 50 | // Run through stackdepot in the child process. |
| 51 | // If any of the hash table cells are locked, this may deadlock. |
| 52 | void child() { |
| 53 | volatile int x = 0; |
| 54 | volatile int v = 0; |
| 55 | dfsan_set_label(label: 16, addr: (void *)&x, size: sizeof(x)); |
| 56 | for (int i = 0; i < 10000; ++i) { |
| 57 | v = x; |
| 58 | x = v; |
| 59 | } |
| 60 | write(fd: 2, buf: "done\n" , 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(newthread: &t[i], NULL, start_routine: copy_labels_thread, arg: (void *)(long)i); |
| 68 | usleep(useconds: 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 | } |
| 76 | } else { |
| 77 | // child |
| 78 | child(); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | int main() { |
| 83 | const int kChildren = 20; |
| 84 | for (int i = 0; i < kChildren; ++i) { |
| 85 | pid_t pid = fork(); |
| 86 | assert(dfsan_get_label(pid) == 0); |
| 87 | if (pid) { |
| 88 | // parent |
| 89 | } else { |
| 90 | test(); |
| 91 | exit(status: 0); |
| 92 | } |
| 93 | } |
| 94 | |
| 95 | for (int i = 0; i < kChildren; ++i) { |
| 96 | pid_t p; |
| 97 | while ((p = wait(NULL)) == -1) { |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | return 0; |
| 102 | } |
| 103 | |
| 104 | // Expect 20 (== kChildren) "done" messages. |
| 105 | // CHECK-COUNT-20: done |
| 106 | |