]>
Commit | Line | Data |
---|---|---|
c3c9b80d A |
1 | #include <assert.h> |
2 | #include <stdio.h> | |
3 | #include <pthread.h> | |
4 | #include <signal.h> | |
5 | #include <unistd.h> | |
6 | #include <errno.h> | |
7 | #include <string.h> | |
8 | #include <sys/wait.h> | |
9 | ||
10 | #include <darwintest.h> | |
11 | ||
12 | // rdar://58566604 | |
13 | // Exercise races of signal delivery vs exec in multi-threaded processes | |
14 | ||
15 | T_GLOBAL_META(T_META_NAMESPACE("xnu.exec"), | |
16 | T_META_CHECK_LEAKS(false), | |
17 | T_META_ALL_VALID_ARCHS(true)); | |
18 | ||
19 | enum { KILL_ONCE, KILL_MANY, KILL_LAST } kill_mode; | |
20 | enum { EXEC_FIRST, EXEC_SECOND, EXEC_LAST } exec_mode; | |
21 | ||
22 | static int fd[2]; | |
23 | ||
24 | static void | |
25 | do_exec(void) | |
26 | { | |
27 | char echo_arg[50] = ""; | |
28 | ||
29 | snprintf(echo_arg, sizeof(echo_arg), " Child[%d] says hello after exec", getpid()); | |
30 | ||
31 | char * new_argv[] = { | |
32 | "/bin/echo", | |
33 | echo_arg, | |
34 | NULL | |
35 | }; | |
36 | ||
37 | int ret = execv(new_argv[0], new_argv); | |
38 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "execv()"); | |
39 | } | |
40 | ||
41 | static void* | |
42 | thread_main(void* arg) | |
43 | { | |
44 | T_LOG("mode: %d, %d: Child[%d] created second thread\n", | |
45 | kill_mode, exec_mode, getpid()); | |
46 | ||
47 | if (exec_mode == EXEC_SECOND) { | |
48 | int ret = dprintf(fd[1], "Hi!"); | |
49 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dprintf()"); | |
50 | do_exec(); | |
51 | } | |
52 | ||
53 | while (1) { | |
54 | } | |
55 | return NULL; | |
56 | } | |
57 | ||
58 | void | |
59 | run_test(void) | |
60 | { | |
61 | T_LOG("mode: %d, %d: Parent[%d]: forking\n", | |
62 | kill_mode, exec_mode, getpid()); | |
63 | ||
64 | pid_t child_pid = fork(); | |
65 | ||
66 | T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "fork()"); | |
67 | ||
68 | int ret = 0; | |
69 | ||
70 | if (child_pid == 0) { | |
71 | pthread_t thread; | |
72 | ret = pthread_create(&thread, NULL, thread_main, NULL); | |
73 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "pthread_create()"); | |
74 | ||
75 | if (exec_mode == EXEC_FIRST) { | |
76 | ret = dprintf(fd[1], "Hi!"); | |
77 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "dprintf()"); | |
78 | ||
79 | do_exec(); | |
80 | } | |
81 | ||
82 | while (1) { | |
83 | } | |
84 | } else { | |
85 | char buffer[4] = ""; | |
86 | ret = read(fd[0], buffer, sizeof(buffer)); | |
87 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "read()"); | |
88 | ||
89 | T_LOG("mode: %d, %d: Parent[%d]: got: '%s' from execing child, trying to kill and wait\n", | |
90 | kill_mode, exec_mode, getpid(), buffer); | |
91 | ||
92 | int killcount = 0, status = 0, waitedpid = 0; | |
93 | ||
94 | switch (kill_mode) { | |
95 | case KILL_ONCE: | |
96 | ret = kill(child_pid, SIGKILL); | |
97 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kill()"); | |
98 | ||
99 | waitedpid = waitpid(child_pid, &status, 0); | |
100 | ||
101 | T_QUIET; T_ASSERT_POSIX_SUCCESS(waitedpid, "waitpid()"); | |
102 | ||
103 | killcount++; | |
104 | break; | |
105 | case KILL_MANY: | |
106 | while (waitedpid == 0) { | |
107 | ret = kill(child_pid, SIGKILL); | |
108 | T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "kill()"); | |
109 | ||
110 | waitedpid = waitpid(child_pid, &status, WNOHANG); | |
111 | T_QUIET; T_ASSERT_POSIX_SUCCESS(waitedpid, "waitpid()"); | |
112 | ||
113 | killcount++; | |
114 | } | |
115 | break; | |
116 | default: | |
117 | break; | |
118 | } | |
119 | ||
120 | T_LOG("mode: %d, %d: Parent[%d]: waitpid returned: %d, errno %d (%s), exit signal %d, after %d loops\n", | |
121 | kill_mode, exec_mode, getpid(), waitedpid, errno, strerror(errno), WTERMSIG(status), killcount); | |
122 | } | |
123 | } | |
124 | ||
125 | T_DECL(exec_exit_race_once_first, "Exec-exit race, one kill, exec on first thread") { | |
126 | int rv = pipe(fd); | |
127 | T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pipe()"); | |
128 | ||
129 | kill_mode = KILL_ONCE; | |
130 | exec_mode = EXEC_FIRST; | |
131 | ||
132 | for (int i = 0; i < 1000; i++) { | |
133 | run_test(); | |
134 | } | |
135 | } | |
136 | ||
137 | T_DECL(exec_exit_race_many_first, "Exec-exit race, many kill, exec on first thread") { | |
138 | int rv = pipe(fd); | |
139 | T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pipe()"); | |
140 | ||
141 | kill_mode = KILL_MANY; | |
142 | exec_mode = EXEC_FIRST; | |
143 | ||
144 | for (int i = 0; i < 1000; i++) { | |
145 | run_test(); | |
146 | } | |
147 | } | |
148 | ||
149 | T_DECL(exec_exit_race_once_second, "Exec-exit race, one kill, exec on second thread") { | |
150 | int rv = pipe(fd); | |
151 | T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pipe()"); | |
152 | ||
153 | kill_mode = KILL_ONCE; | |
154 | exec_mode = EXEC_SECOND; | |
155 | ||
156 | for (int i = 0; i < 1000; i++) { | |
157 | run_test(); | |
158 | } | |
159 | } | |
160 | ||
161 | T_DECL(exec_exit_race_many_second, "Exec-exit race, many kill, exec on second thread") { | |
162 | int rv = pipe(fd); | |
163 | T_QUIET; T_ASSERT_POSIX_SUCCESS(rv, "pipe()"); | |
164 | ||
165 | kill_mode = KILL_MANY; | |
166 | exec_mode = EXEC_SECOND; | |
167 | ||
168 | for (int i = 0; i < 1000; i++) { | |
169 | run_test(); | |
170 | } | |
171 | } |