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 <[email protected]>
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
15 #include "tst_test.h"
16 #include "clone_platform.h"
17 #include "lapi/syscalls.h"
18 #include "lapi/futex.h"
19
20 static pid_t ptid, ctid, tgid;
21 static void *child_stack;
22
23 static void test_clone_parent(int t);
24 static int child_clone_parent(void *);
25 static pid_t parent_ppid;
26
27 static void test_clone_tid(int t);
28 static int child_clone_child_settid(void *);
29 static int child_clone_parent_settid(void *);
30
31
32 static void test_clone_thread(int t);
33 static int child_clone_thread(void *);
34
35 /*
36 * Children cloned with CLONE_VM should avoid using any functions that
37 * might require dl_runtime_resolve, because they share thread-local
38 * storage with parent. If both try to resolve symbols at same time you
39 * can crash, likely at _dl_x86_64_restore_sse().
40 * See this thread for relevant discussion:
41 * http://www.mail-archive.com/[email protected]/msg01944.html
42 */
43 static struct test_case {
44 char *name;
45 int flags;
46 void (*testfunc)(int);
47 int (*do_child)(void *);
48 } test_cases[] = {
49 {"CLONE_PARENT", CLONE_PARENT | SIGCHLD,
50 test_clone_parent, child_clone_parent},
51 {"CLONE_CHILD_SETTID", CLONE_CHILD_SETTID | SIGCHLD,
52 test_clone_tid, child_clone_child_settid},
53 {"CLONE_PARENT_SETTID", CLONE_PARENT_SETTID | CLONE_VM | SIGCHLD,
54 test_clone_tid, child_clone_parent_settid},
55 {"CLONE_THREAD", CLONE_THREAD | CLONE_SIGHAND | CLONE_VM |
56 CLONE_CHILD_CLEARTID | SIGCHLD,
57 test_clone_thread, child_clone_thread},
58 };
59
do_test(unsigned int i)60 static void do_test(unsigned int i)
61 {
62 tst_res(TINFO, "running %s", test_cases[i].name);
63 test_cases[i].testfunc(i);
64 }
65
setup(void)66 static void setup(void)
67 {
68 child_stack = SAFE_MALLOC(CHILD_STACK_SIZE);
69 }
70
cleanup(void)71 static void cleanup(void)
72 {
73 free(child_stack);
74 }
75
clone_child(const struct test_case * t)76 static long clone_child(const struct test_case *t)
77 {
78 TEST(ltp_clone7(t->flags, t->do_child, NULL, CHILD_STACK_SIZE,
79 child_stack, &ptid, NULL, &ctid));
80
81 if (TST_RET == -1 && TTERRNO == ENOSYS)
82 tst_brk(TCONF, "clone does not support 7 args");
83
84 if (TST_RET == -1)
85 tst_brk(TBROK | TTERRNO, "%s clone() failed", t->name);
86
87 return TST_RET;
88 }
89
test_clone_parent(int t)90 static void test_clone_parent(int t)
91 {
92 pid_t child;
93
94 child = SAFE_FORK();
95 if (!child) {
96 parent_ppid = getppid();
97 clone_child(&test_cases[t]);
98 _exit(0);
99 }
100 tst_reap_children();
101 }
102
child_clone_parent(void * arg LTP_ATTRIBUTE_UNUSED)103 static int child_clone_parent(void *arg LTP_ATTRIBUTE_UNUSED)
104 {
105 if (parent_ppid == getppid()) {
106 tst_res(TPASS, "clone and forked child has the same parent");
107 } else {
108 tst_res(TFAIL, "getppid != parent_ppid (%d != %d)",
109 parent_ppid, getppid());
110 }
111 tst_syscall(__NR_exit, 0);
112 return 0;
113 }
114
test_clone_tid(int t)115 static void test_clone_tid(int t)
116 {
117 clone_child(&test_cases[t]);
118 tst_reap_children();
119 }
120
child_clone_child_settid(void * arg LTP_ATTRIBUTE_UNUSED)121 static int child_clone_child_settid(void *arg LTP_ATTRIBUTE_UNUSED)
122 {
123 if (ctid == tst_syscall(__NR_getpid))
124 tst_res(TPASS, "clone() correctly set ctid");
125 else
126 tst_res(TFAIL, "ctid != getpid() (%d != %d)", ctid, getpid());
127 tst_syscall(__NR_exit, 0);
128 return 0;
129 }
130
child_clone_parent_settid(void * arg LTP_ATTRIBUTE_UNUSED)131 static int child_clone_parent_settid(void *arg LTP_ATTRIBUTE_UNUSED)
132 {
133 if (ptid == tst_syscall(__NR_getpid))
134 tst_res(TPASS, "clone() correctly set ptid");
135 else
136 tst_res(TFAIL, "ptid != getpid() (%d != %d)", ptid, getpid());
137 tst_syscall(__NR_exit, 0);
138 return 0;
139 }
140
test_clone_thread(int t)141 static void test_clone_thread(int t)
142 {
143 pid_t child;
144
145 child = SAFE_FORK();
146 if (!child) {
147 struct timespec timeout = { 5 /* sec */, 0 };
148
149 tgid = tst_syscall(__NR_getpid);
150 ctid = -1;
151
152 clone_child(&test_cases[t]);
153
154 if (syscall(SYS_futex, &ctid, FUTEX_WAIT, -1, &timeout)) {
155 /*
156 * futex here is racing with clone() above.
157 * Which means we can get EWOULDBLOCK if
158 * ctid has been already changed by clone()
159 * before we make the call. As long as ctid
160 * changes we should not report error when
161 * futex returns EWOULDBLOCK.
162 */
163 if (errno != EWOULDBLOCK || ctid == -1) {
164 tst_res(TFAIL | TERRNO,
165 "futex failed, ctid: %d", ctid);
166 _exit(0);
167 }
168 }
169 tst_res(TPASS, "futex exit on ctid change, ctid: %d", ctid);
170 _exit(0);
171 }
172
173 tst_reap_children();
174 }
175
child_clone_thread(void * arg LTP_ATTRIBUTE_UNUSED)176 static int child_clone_thread(void *arg LTP_ATTRIBUTE_UNUSED)
177 {
178 if (tgid == tst_syscall(__NR_getpid))
179 tst_res(TPASS, "clone has the same thread id");
180 else
181 tst_res(TFAIL, "clone's thread id not equal parent's id");
182 tst_syscall(__NR_exit, 0);
183 return 0;
184 }
185
186 static struct tst_test test = {
187 .tcnt = ARRAY_SIZE(test_cases),
188 .test = do_test,
189 .setup = setup,
190 .cleanup = cleanup,
191 .forks_child = 1
192 };
193