1 /*
2 * Copyright (c) 2013 Fujitsu Ltd.
3 * Author: Zeng Linggang <zenglg.jy@cn.fujitsu.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program.
15 */
16
17 #define _GNU_SOURCE
18 #include <errno.h>
19 #include <sched.h>
20 #include <sys/wait.h>
21 #include "test.h"
22 #include "clone_platform.h"
23 #include "safe_macros.h"
24 #include "linux_syscall_numbers.h"
25
26 char *TCID = "clone08";
27
28 static pid_t ptid, ctid, tgid;
29 static void *child_stack;
30
31 static void setup(void);
32 static void cleanup(void);
33
34 static void test_clone_parent(int t);
35 static int child_clone_parent(void);
36 static pid_t parent_ppid;
37
38 static void test_clone_tid(int t);
39 static int child_clone_child_settid(void);
40 static int child_clone_parent_settid(void);
41
42 #ifdef CLONE_STOPPED
43 static void test_clone_stopped(int t);
44 static int child_clone_stopped(void);
45 static int stopped_flag;
46 #endif
47
48 static void test_clone_thread(int t);
49 static int child_clone_thread(void);
50 static int tst_result;
51
52 /*
53 * Children cloned with CLONE_VM should avoid using any functions that
54 * might require dl_runtime_resolve, because they share thread-local
55 * storage with parent. If both try to resolve symbols at same time you
56 * can crash, likely at _dl_x86_64_restore_sse().
57 * See this thread for relevant discussion:
58 * http://www.mail-archive.com/utrace-devel@redhat.com/msg01944.html
59 */
60 static struct test_case {
61 char *name;
62 int flags;
63 void (*testfunc)(int);
64 int (*do_child)();
65 } test_cases[] = {
66 {"CLONE_PARENT", CLONE_PARENT | SIGCHLD,
67 test_clone_parent, child_clone_parent},
68 {"CLONE_CHILD_SETTID", CLONE_CHILD_SETTID | SIGCHLD,
69 test_clone_tid, child_clone_child_settid},
70 {"CLONE_PARENT_SETTID", CLONE_PARENT_SETTID | CLONE_VM | SIGCHLD,
71 test_clone_tid, child_clone_parent_settid},
72 #ifdef CLONE_STOPPED
73 {"CLONE_STOPPED", CLONE_STOPPED | CLONE_VM | SIGCHLD,
74 test_clone_stopped, child_clone_stopped},
75 #endif
76 {"CLONE_THREAD", CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | SIGCHLD,
77 test_clone_thread, child_clone_thread},
78 };
79
80 int TST_TOTAL = ARRAY_SIZE(test_cases);
81
main(int ac,char ** av)82 int main(int ac, char **av)
83 {
84 int i, lc;
85
86 tst_parse_opts(ac, av, NULL, NULL);
87
88 setup();
89 for (lc = 0; TEST_LOOPING(lc); lc++) {
90 tst_count = 0;
91 for (i = 0; i < TST_TOTAL; i++) {
92 tst_resm(TINFO, "running %s", test_cases[i].name);
93 test_cases[i].testfunc(i);
94 }
95 }
96 cleanup();
97 tst_exit();
98 }
99
setup(void)100 static void setup(void)
101 {
102 tst_sig(FORK, DEF_HANDLER, cleanup);
103
104 TEST_PAUSE;
105
106 tst_tmpdir();
107
108 child_stack = SAFE_MALLOC(cleanup, CHILD_STACK_SIZE);
109 }
110
cleanup(void)111 static void cleanup(void)
112 {
113 free(child_stack);
114
115 tst_rmdir();
116 }
117
clone_child(const struct test_case * t,int use_tst)118 static long clone_child(const struct test_case *t, int use_tst)
119 {
120 TEST(ltp_clone7(t->flags, t->do_child, NULL, CHILD_STACK_SIZE,
121 child_stack, &ptid, NULL, &ctid));
122
123 if (TEST_RETURN == -1 && TTERRNO == ENOSYS)
124 tst_brkm(TCONF, cleanup, "clone does not support 7 args");
125
126 if (TEST_RETURN == -1) {
127 if (use_tst) {
128 tst_brkm(TBROK | TTERRNO, cleanup, "%s clone() failed",
129 t->name);
130 } else {
131 printf("%s clone() failed, errno: %d",
132 t->name, TEST_ERRNO);
133 exit(1);
134 }
135 }
136 return TEST_RETURN;
137 }
138
wait4child(pid_t child)139 static int wait4child(pid_t child)
140 {
141 int status;
142
143 if (waitpid(child, &status, 0) == -1)
144 tst_resm(TBROK|TERRNO, "waitpid");
145 if (WIFEXITED(status))
146 return WEXITSTATUS(status);
147 else
148 return status;
149 }
150
test_clone_parent(int t)151 static void test_clone_parent(int t)
152 {
153 int status;
154 pid_t child;
155
156 fflush(stdout);
157 child = FORK_OR_VFORK();
158 switch (child) {
159 case 0:
160 parent_ppid = getppid();
161 clone_child(&test_cases[t], 0);
162 exit(0);
163 case -1:
164 tst_brkm(TBROK | TERRNO, NULL, "test_clone_parent fork");
165 default:
166 status = wait4child(child);
167 if (status == 0) {
168 /* wait for CLONE_PARENT child */
169 status = wait4child(-1);
170 if (status == 0) {
171 tst_resm(TPASS, "test %s", test_cases[t].name);
172 } else {
173 tst_resm(TFAIL, "test %s, status: %d",
174 test_cases[t].name, status);
175 }
176 } else {
177 tst_resm(TFAIL, "test %s, status: %d",
178 test_cases[t].name, status);
179 }
180 };
181 }
182
child_clone_parent(void)183 static int child_clone_parent(void)
184 {
185 if (parent_ppid == getppid())
186 exit(0);
187 printf("FAIL: getppid != parent_ppid (%d != %d)\n",
188 parent_ppid, getppid());
189 exit(1);
190 }
191
test_clone_tid(int t)192 static void test_clone_tid(int t)
193 {
194 int status;
195 pid_t child;
196
197 child = clone_child(&test_cases[t], 1);
198 status = wait4child(child);
199 if (status == 0) {
200 tst_resm(TPASS, "test %s", test_cases[t].name);
201 } else {
202 tst_resm(TFAIL, "test %s, status: %d",
203 test_cases[t].name, status);
204 }
205 }
206
child_clone_child_settid(void)207 static int child_clone_child_settid(void)
208 {
209 if (ctid == ltp_syscall(__NR_getpid))
210 ltp_syscall(__NR_exit, 0);
211 printf("FAIL: ctid != getpid() (%d != %d)\n",
212 ctid, getpid());
213 ltp_syscall(__NR_exit, 1);
214 return 0;
215 }
216
child_clone_parent_settid(void)217 static int child_clone_parent_settid(void)
218 {
219 if (ptid == ltp_syscall(__NR_getpid))
220 ltp_syscall(__NR_exit, 0);
221 printf("FAIL: ptid != getpid() (%d != %d)\n",
222 ptid, getpid());
223 ltp_syscall(__NR_exit, 1);
224 return 0;
225 }
226
227 #ifdef CLONE_STOPPED
test_clone_stopped(int t)228 static void test_clone_stopped(int t)
229 {
230 int i;
231 int status;
232 int flag;
233 pid_t child;
234
235 if (tst_kvercmp(2, 6, 38) >= 0) {
236 tst_resm(TINFO, "CLONE_STOPPED skipped for kernels >= 2.6.38");
237 return;
238 }
239
240 stopped_flag = 0;
241 child = clone_child(&test_cases[t], 1);
242
243 /* give the kernel scheduler chance to run the CLONE_STOPPED thread*/
244 for (i = 0; i < 100; i++) {
245 sched_yield();
246 usleep(1000);
247 }
248
249 flag = stopped_flag;
250 if (kill(child, SIGCONT) != 0)
251 tst_brkm(TBROK | TERRNO, cleanup, "kill SIGCONT failed");
252
253 status = wait4child(child);
254 if (status == 0 && flag == 0) {
255 tst_resm(TPASS, "test %s", test_cases[t].name);
256 } else {
257 tst_resm(TFAIL, "test %s, status: %d, flag: %d",
258 test_cases[t].name, status, flag);
259 }
260 }
261
child_clone_stopped(void)262 static int child_clone_stopped(void)
263 {
264 stopped_flag = 1;
265 ltp_syscall(__NR_exit, 0);
266 return 0;
267 }
268 #endif
269
test_clone_thread(int t)270 static void test_clone_thread(int t)
271 {
272 pid_t child;
273 int i, status;
274
275 fflush(stdout);
276 child = FORK_OR_VFORK();
277 switch (child) {
278 case 0:
279 tgid = ltp_syscall(__NR_getpid);
280 tst_result = -1;
281 clone_child(&test_cases[t], 0);
282
283 for (i = 0; i < 5000; i++) {
284 sched_yield();
285 usleep(1000);
286 if (tst_result != -1)
287 break;
288 }
289 ltp_syscall(__NR_exit, tst_result);
290 case -1:
291 tst_brkm(TBROK | TERRNO, NULL, "test_clone_thread fork");
292 default:
293 status = wait4child(child);
294 if (status == 0) {
295 tst_resm(TPASS, "test %s", test_cases[t].name);
296 } else {
297 tst_resm(TFAIL, "test %s, status: %d",
298 test_cases[t].name, status);
299 }
300 };
301 }
302
child_clone_thread(void)303 static int child_clone_thread(void)
304 {
305 if (tgid == ltp_syscall(__NR_getpid))
306 tst_result = TPASS;
307 else
308 tst_result = TFAIL;
309 ltp_syscall(__NR_exit, 0);
310 return 0;
311 }
312