1 | // RUN: %clang -O0 %s -o %t && %env_tool_opts=die_after_fork=0 %run %t |
2 | |
3 | // The test uses pthread barriers which are not available on Darwin. |
4 | // UNSUPPORTED: darwin |
5 | |
6 | // FIXME: It probably hangs on this platform. |
7 | // UNSUPPORTED: ppc |
8 | |
9 | // FIXME: TSAN does not lock allocator. |
10 | // UNSUPPORTED: tsan |
11 | |
12 | // FIXME: False stack overflow report |
13 | // UNSUPPORTED: android && asan |
14 | |
15 | // FIXME: Requires `FutexWait` implementation. See __asan::InstallAtForkHandler. |
16 | // UNSUPPORTED: target={{.*solaris.*}} |
17 | // UNSUPPORTED: target={{.*netbsd.*}} |
18 | // UNSUPPORTED: target={{.*apple.*}} |
19 | |
20 | // Forking in multithread environment is unsupported. However we already have |
21 | // some workarounds, and will add more, so this is the test. |
22 | // The test try to check two things: |
23 | // 1. Internal mutexes used by `inparent` thread do not deadlock `inchild` |
24 | // thread. |
25 | // 2. Stack poisoned by `inparent` is not poisoned in `inchild` thread. |
26 | |
27 | #include <assert.h> |
28 | #include <pthread.h> |
29 | #include <stdint.h> |
30 | #include <stdio.h> |
31 | #include <stdlib.h> |
32 | #include <sys/types.h> |
33 | #include <sys/wait.h> |
34 | #include <unistd.h> |
35 | |
36 | #include "sanitizer_common/sanitizer_specific.h" |
37 | |
38 | static const size_t kBufferSize = 8192; |
39 | |
40 | pthread_barrier_t bar; |
41 | |
42 | // Without appropriate workarounds this code can cause the forked process to |
43 | // start with locked internal mutexes. |
44 | void ShouldNotDeadlock() { |
45 | // Don't bother with leaks, we try to trigger allocator or lsan deadlock. |
46 | __lsan_disable(); |
47 | void *volatile p = malloc(size: 10); |
48 | __lsan_do_recoverable_leak_check(); |
49 | // Allocator still in broken state, `free` may report errors. |
50 | // free(p); |
51 | __lsan_enable(); |
52 | } |
53 | |
54 | // Prevent stack buffer cleanup by instrumentation. |
55 | #define NOSAN __attribute__((no_sanitize("address", "hwaddress", "memory"))) |
56 | |
57 | NOSAN static void *inparent(void *arg) { |
58 | char t[kBufferSize]; |
59 | make_mem_bad(t, sizeof(t)); |
60 | |
61 | pthread_barrier_wait(barrier: &bar); |
62 | |
63 | for (;;) |
64 | ShouldNotDeadlock(); |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | NOSAN static void *inchild(void *arg) { |
70 | char t[kBufferSize]; |
71 | check_mem_is_good(t, sizeof(t)); |
72 | ShouldNotDeadlock(); |
73 | return 0; |
74 | } |
75 | |
76 | int main(void) { |
77 | #if __has_feature(hwaddress_sanitizer) |
78 | __hwasan_enable_allocator_tagging(); |
79 | #endif |
80 | |
81 | pid_t pid; |
82 | |
83 | pthread_barrier_init(barrier: &bar, NULL, count: 2); |
84 | pthread_t thread_id; |
85 | while (pthread_create(newthread: &thread_id, attr: 0, start_routine: &inparent, arg: 0) != 0) { |
86 | } |
87 | pthread_barrier_wait(barrier: &bar); |
88 | |
89 | pid = fork(); |
90 | switch (pid) { |
91 | case -1: |
92 | perror(s: "fork" ); |
93 | return -1; |
94 | case 0: |
95 | ShouldNotDeadlock(); |
96 | while (pthread_create(newthread: &thread_id, attr: 0, start_routine: &inchild, arg: 0) != 0) { |
97 | } |
98 | pthread_join(th: thread_id, NULL); |
99 | _exit(status: 0); |
100 | break; |
101 | default: { |
102 | int status; |
103 | pid_t child = waitpid(pid: pid, stat_loc: &status, /*options=*/0); |
104 | assert(pid == child); |
105 | assert(WIFEXITED(status)); |
106 | assert(WEXITSTATUS(status) == 0); |
107 | break; |
108 | } |
109 | } |
110 | |
111 | return 0; |
112 | } |
113 | |