• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2017 Oracle and/or its affiliates. All Rights Reserved.
4  * Copyright (c) 2013 Fujitsu Ltd.
5  * Author: Zeng Linggang <zenglg.jy@cn.fujitsu.com>
6  */
7 
8 #define _GNU_SOURCE
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <errno.h>
12 #include <sched.h>
13 #include <sys/wait.h>
14 #include <linux/futex.h>
15 
16 #include "tst_test.h"
17 #include "clone_platform.h"
18 #include "lapi/syscalls.h"
19 #include "lapi/futex.h"
20 
21 static pid_t ptid, ctid, tgid;
22 static void *child_stack;
23 
24 static void test_clone_parent(int t);
25 static int child_clone_parent(void *);
26 static pid_t parent_ppid;
27 
28 static void test_clone_tid(int t);
29 static int child_clone_child_settid(void *);
30 static int child_clone_parent_settid(void *);
31 
32 #ifdef CLONE_STOPPED
33 static void test_clone_stopped(int t);
34 static int child_clone_stopped(void *);
35 static int stopped_flag;
36 #endif
37 
38 static void test_clone_thread(int t);
39 static int child_clone_thread(void *);
40 
41 /*
42  * Children cloned with CLONE_VM should avoid using any functions that
43  * might require dl_runtime_resolve, because they share thread-local
44  * storage with parent. If both try to resolve symbols at same time you
45  * can crash, likely at _dl_x86_64_restore_sse().
46  * See this thread for relevant discussion:
47  * http://www.mail-archive.com/utrace-devel@redhat.com/msg01944.html
48  */
49 static struct test_case {
50 	char *name;
51 	int flags;
52 	void (*testfunc)(int);
53 	int (*do_child)(void *);
54 } test_cases[] = {
55 	{"CLONE_PARENT", CLONE_PARENT | SIGCHLD,
56 	 test_clone_parent, child_clone_parent},
57 	{"CLONE_CHILD_SETTID", CLONE_CHILD_SETTID | SIGCHLD,
58 	 test_clone_tid, child_clone_child_settid},
59 	{"CLONE_PARENT_SETTID", CLONE_PARENT_SETTID | CLONE_VM | SIGCHLD,
60 	 test_clone_tid, child_clone_parent_settid},
61 #ifdef CLONE_STOPPED
62 	{"CLONE_STOPPED", CLONE_STOPPED | CLONE_VM | SIGCHLD,
63 	 test_clone_stopped, child_clone_stopped},
64 #endif
65 	{"CLONE_THREAD", CLONE_THREAD | CLONE_SIGHAND | CLONE_VM |
66 	 CLONE_CHILD_CLEARTID | SIGCHLD,
67 	 test_clone_thread, child_clone_thread},
68 };
69 
do_test(unsigned int i)70 static void do_test(unsigned int i)
71 {
72 	tst_res(TINFO, "running %s", test_cases[i].name);
73 	test_cases[i].testfunc(i);
74 }
75 
setup(void)76 static void setup(void)
77 {
78 	child_stack = SAFE_MALLOC(CHILD_STACK_SIZE);
79 }
80 
cleanup(void)81 static void cleanup(void)
82 {
83 	free(child_stack);
84 }
85 
clone_child(const struct test_case * t)86 static long clone_child(const struct test_case *t)
87 {
88 	TEST(ltp_clone7(t->flags, t->do_child, NULL, CHILD_STACK_SIZE,
89 		child_stack, &ptid, NULL, &ctid));
90 
91 	if (TST_RET == -1 && TTERRNO == ENOSYS)
92 		tst_brk(TCONF, "clone does not support 7 args");
93 
94 	if (TST_RET == -1)
95 		tst_brk(TBROK | TTERRNO, "%s clone() failed", t->name);
96 
97 	return TST_RET;
98 }
99 
test_clone_parent(int t)100 static void test_clone_parent(int t)
101 {
102 	pid_t child;
103 
104 	child = SAFE_FORK();
105 	if (!child) {
106 		parent_ppid = getppid();
107 		clone_child(&test_cases[t]);
108 		_exit(0);
109 	}
110 	tst_reap_children();
111 }
112 
child_clone_parent(void * arg LTP_ATTRIBUTE_UNUSED)113 static int child_clone_parent(void *arg LTP_ATTRIBUTE_UNUSED)
114 {
115 	if (parent_ppid == getppid()) {
116 		tst_res(TPASS, "clone and forked child has the same parent");
117 	} else {
118 		tst_res(TFAIL, "getppid != parent_ppid (%d != %d)",
119 			parent_ppid, getppid());
120 	}
121 	tst_syscall(__NR_exit, 0);
122 	return 0;
123 }
124 
test_clone_tid(int t)125 static void test_clone_tid(int t)
126 {
127 	clone_child(&test_cases[t]);
128 	tst_reap_children();
129 }
130 
child_clone_child_settid(void * arg LTP_ATTRIBUTE_UNUSED)131 static int child_clone_child_settid(void *arg LTP_ATTRIBUTE_UNUSED)
132 {
133 	if (ctid == tst_syscall(__NR_getpid))
134 		tst_res(TPASS, "clone() correctly set ctid");
135 	else
136 		tst_res(TFAIL, "ctid != getpid() (%d != %d)", ctid, getpid());
137 	tst_syscall(__NR_exit, 0);
138 	return 0;
139 }
140 
child_clone_parent_settid(void * arg LTP_ATTRIBUTE_UNUSED)141 static int child_clone_parent_settid(void *arg LTP_ATTRIBUTE_UNUSED)
142 {
143 	if (ptid == tst_syscall(__NR_getpid))
144 		tst_res(TPASS, "clone() correctly set ptid");
145 	else
146 		tst_res(TFAIL, "ptid != getpid() (%d != %d)", ptid, getpid());
147 	tst_syscall(__NR_exit, 0);
148 	return 0;
149 }
150 
151 #ifdef CLONE_STOPPED
test_clone_stopped(int t)152 static void test_clone_stopped(int t)
153 {
154 	pid_t child;
155 
156 	if (tst_kvercmp(2, 6, 38) >= 0) {
157 		tst_res(TCONF, "CLONE_STOPPED skipped for kernels >= 2.6.38");
158 		return;
159 	}
160 
161 	child = clone_child(&test_cases[t]);
162 
163 	TST_PROCESS_STATE_WAIT(child, 'T', 0);
164 
165 	stopped_flag = 0;
166 
167 	SAFE_KILL(child, SIGCONT);
168 
169 	tst_reap_children();
170 
171 	if (stopped_flag == 1)
172 		tst_res(TPASS, "clone stopped and resumed as expected");
173 	else
174 		tst_res(TFAIL, "clone not stopped, flag %d", stopped_flag);
175 }
176 
child_clone_stopped(void * arg LTP_ATTRIBUTE_UNUSED)177 static int child_clone_stopped(void *arg LTP_ATTRIBUTE_UNUSED)
178 {
179 	stopped_flag = 1;
180 	tst_syscall(__NR_exit, 0);
181 	return 0;
182 }
183 #endif
184 
test_clone_thread(int t)185 static void test_clone_thread(int t)
186 {
187 	pid_t child;
188 
189 	child = SAFE_FORK();
190 	if (!child) {
191 		struct timespec timeout = { 5 /* sec */, 0 };
192 
193 		tgid = tst_syscall(__NR_getpid);
194 		ctid = -1;
195 
196 		clone_child(&test_cases[t]);
197 
198 		if (syscall(SYS_futex, &ctid, FUTEX_WAIT, -1, &timeout)) {
199 			/*
200 			 * futex here is racing with clone() above.
201 			 * Which means we can get EWOULDBLOCK if
202 			 * ctid has been already changed by clone()
203 			 * before we make the call. As long as ctid
204 			 * changes we should not report error when
205 			 * futex returns EWOULDBLOCK.
206 			 */
207 			if (errno != EWOULDBLOCK || ctid == -1) {
208 				tst_res(TFAIL | TERRNO,
209 					"futex failed, ctid: %d", ctid);
210 				_exit(0);
211 			}
212 		}
213 		tst_res(TPASS, "futex exit on ctid change, ctid: %d", ctid);
214 		_exit(0);
215 	}
216 
217 	tst_reap_children();
218 }
219 
child_clone_thread(void * arg LTP_ATTRIBUTE_UNUSED)220 static int child_clone_thread(void *arg LTP_ATTRIBUTE_UNUSED)
221 {
222 	if (tgid == tst_syscall(__NR_getpid))
223 		tst_res(TPASS, "clone has the same thread id");
224 	else
225 		tst_res(TFAIL, "clone's thread id not equal parent's id");
226 	tst_syscall(__NR_exit, 0);
227 	return 0;
228 }
229 
230 static struct tst_test test = {
231 	.tcnt = ARRAY_SIZE(test_cases),
232 	.test = do_test,
233 	.setup = setup,
234 	.cleanup = cleanup,
235 	.forks_child = 1
236 };
237