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