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