• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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