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
26char *TCID = "clone08";
27
28static pid_t ptid, ctid, tgid;
29static void *child_stack;
30
31static void setup(void);
32static void cleanup(void);
33
34static void test_clone_parent(int t);
35static int child_clone_parent(void);
36static pid_t parent_ppid;
37
38static void test_clone_tid(int t);
39static int child_clone_child_settid(void);
40static int child_clone_parent_settid(void);
41
42#ifdef CLONE_STOPPED
43static void test_clone_stopped(int t);
44static int child_clone_stopped(void);
45static int stopped_flag;
46#endif
47
48static void test_clone_thread(int t);
49static int child_clone_thread(void);
50static 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 */
60static 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
80int TST_TOTAL = ARRAY_SIZE(test_cases);
81
82int 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
100static 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
111static void cleanup(void)
112{
113	free(child_stack);
114
115	tst_rmdir();
116}
117
118static 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
139static 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
151static 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
183static 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
192static 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
207static 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
217static 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
228static 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
262static int child_clone_stopped(void)
263{
264	stopped_flag = 1;
265	ltp_syscall(__NR_exit, 0);
266	return 0;
267}
268#endif
269
270static 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
303static 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