xref: /llvm-project/compiler-rt/test/sanitizer_common/TestCases/Posix/fork_threaded.c (revision 1254259e325428c5912843aa94f6fc663a40ea1b)
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: False stack overflow report
10 // UNSUPPORTED: android && asan
11 
12 // FIXME: Requires `FutexWait` implementation. See __asan::InstallAtForkHandler.
13 // UNSUPPORTED: target={{.*solaris.*}}
14 // UNSUPPORTED: target={{.*netbsd.*}}
15 // UNSUPPORTED: target={{.*apple.*}}
16 
17 // Forking in multithread environment is unsupported. However we already have
18 // some workarounds, and will add more, so this is the test.
19 // The test try to check two things:
20 //  1. Internal mutexes used by `inparent` thread do not deadlock `inchild`
21 //     thread.
22 //  2. Stack poisoned by `inparent` is not poisoned in `inchild` thread.
23 
24 // Stack tagging is unsupported.
25 // UNSUPPORTED: hwasan-aliasing
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(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(&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(&bar, NULL, 2);
84   pthread_t thread_id;
85   while (pthread_create(&thread_id, 0, &inparent, 0) != 0) {
86   }
87   pthread_barrier_wait(&bar);
88 
89   pid = fork();
90   switch (pid) {
91   case -1:
92     perror("fork");
93     return -1;
94   case 0:
95     ShouldNotDeadlock();
96     while (pthread_create(&thread_id, 0, &inchild, 0) != 0) {
97     }
98     pthread_join(thread_id, NULL);
99     _exit(0);
100     break;
101   default: {
102     int status;
103     pid_t child = waitpid(pid, &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