1// SPDX-License-Identifier: GPL-2.0
2
3/* Based on Christian Brauner's clone3() example */
4
5#define _GNU_SOURCE
6#include <errno.h>
7#include <inttypes.h>
8#include <linux/types.h>
9#include <linux/sched.h>
10#include <stdbool.h>
11#include <stdint.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <sys/syscall.h>
15#include <sys/types.h>
16#include <sys/un.h>
17#include <sys/wait.h>
18#include <unistd.h>
19#include <sched.h>
20
21#include "../kselftest.h"
22#include "clone3_selftests.h"
23
24enum test_mode {
25 CLONE3_ARGS_NO_TEST,
26 CLONE3_ARGS_ALL_0,
27 CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
28 CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
29 CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
30 CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
31};
32
33static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
34{
35 struct __clone_args args = {
36 .flags = flags,
37 .exit_signal = SIGCHLD,
38 };
39
40 struct clone_args_extended {
41 struct __clone_args args;
42 __aligned_u64 excess_space[2];
43 } args_ext;
44
45 pid_t pid = -1;
46 int status;
47
48 memset(&args_ext, 0, sizeof(args_ext));
49 if (size > sizeof(struct __clone_args))
50 args_ext.excess_space[1] = 1;
51
52 if (size == 0)
53 size = sizeof(struct __clone_args);
54
55 switch (test_mode) {
56 case CLONE3_ARGS_NO_TEST:
57 /*
58 * Uses default 'flags' and 'SIGCHLD'
59 * assignment.
60 */
61 break;
62 case CLONE3_ARGS_ALL_0:
63 args.flags = 0;
64 args.exit_signal = 0;
65 break;
66 case CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG:
67 args.exit_signal = 0xbadc0ded00000000ULL;
68 break;
69 case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG:
70 args.exit_signal = 0x0000000080000000ULL;
71 break;
72 case CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG:
73 args.exit_signal = 0x0000000000000100ULL;
74 break;
75 case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG:
76 args.exit_signal = 0x00000000000000f0ULL;
77 break;
78 }
79
80 memcpy(&args_ext.args, &args, sizeof(struct __clone_args));
81
82 pid = sys_clone3(args: (struct __clone_args *)&args_ext, size);
83 if (pid < 0) {
84 ksft_print_msg(msg: "%s - Failed to create new process\n",
85 strerror(errno));
86 return -errno;
87 }
88
89 if (pid == 0) {
90 ksft_print_msg(msg: "I am the child, my PID is %d\n", getpid());
91 _exit(EXIT_SUCCESS);
92 }
93
94 ksft_print_msg(msg: "I am the parent (%d). My child's pid is %d\n",
95 getpid(), pid);
96
97 if (waitpid(-1, &status, __WALL) < 0) {
98 ksft_print_msg(msg: "Child returned %s\n", strerror(errno));
99 return -errno;
100 }
101 if (WEXITSTATUS(status))
102 return WEXITSTATUS(status);
103
104 return 0;
105}
106
107static bool test_clone3(uint64_t flags, size_t size, int expected,
108 enum test_mode test_mode)
109{
110 int ret;
111
112 ksft_print_msg(
113 "[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",
114 getpid(), flags, size);
115 ret = call_clone3(flags, size, test_mode);
116 ksft_print_msg(msg: "[%d] clone3() with flags says: %d expected %d\n",
117 getpid(), ret, expected);
118 if (ret != expected) {
119 ksft_print_msg(
120 msg: "[%d] Result (%d) is different than expected (%d)\n",
121 getpid(), ret, expected);
122 return false;
123 }
124
125 return true;
126}
127
128typedef bool (*filter_function)(void);
129typedef size_t (*size_function)(void);
130
131static bool not_root(void)
132{
133 if (getuid() != 0) {
134 ksft_print_msg(msg: "Not running as root\n");
135 return true;
136 }
137
138 return false;
139}
140
141static bool no_timenamespace(void)
142{
143 if (not_root())
144 return true;
145
146 if (!access("/proc/self/ns/time", F_OK))
147 return false;
148
149 ksft_print_msg(msg: "Time namespaces are not supported\n");
150 return true;
151}
152
153static size_t page_size_plus_8(void)
154{
155 return getpagesize() + 8;
156}
157
158struct test {
159 const char *name;
160 uint64_t flags;
161 size_t size;
162 size_function size_function;
163 int expected;
164 enum test_mode test_mode;
165 filter_function filter;
166};
167
168static const struct test tests[] = {
169 {
170 .name = "simple clone3()",
171 .flags = 0,
172 .size = 0,
173 .expected = 0,
174 .test_mode = CLONE3_ARGS_NO_TEST,
175 },
176 {
177 .name = "clone3() in a new PID_NS",
178 .flags = CLONE_NEWPID,
179 .size = 0,
180 .expected = 0,
181 .test_mode = CLONE3_ARGS_NO_TEST,
182 .filter = not_root,
183 },
184 {
185 .name = "CLONE_ARGS_SIZE_VER0",
186 .flags = 0,
187 .size = CLONE_ARGS_SIZE_VER0,
188 .expected = 0,
189 .test_mode = CLONE3_ARGS_NO_TEST,
190 },
191 {
192 .name = "CLONE_ARGS_SIZE_VER0 - 8",
193 .flags = 0,
194 .size = CLONE_ARGS_SIZE_VER0 - 8,
195 .expected = -EINVAL,
196 .test_mode = CLONE3_ARGS_NO_TEST,
197 },
198 {
199 .name = "sizeof(struct clone_args) + 8",
200 .flags = 0,
201 .size = sizeof(struct __clone_args) + 8,
202 .expected = 0,
203 .test_mode = CLONE3_ARGS_NO_TEST,
204 },
205 {
206 .name = "exit_signal with highest 32 bits non-zero",
207 .flags = 0,
208 .size = 0,
209 .expected = -EINVAL,
210 .test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
211 },
212 {
213 .name = "negative 32-bit exit_signal",
214 .flags = 0,
215 .size = 0,
216 .expected = -EINVAL,
217 .test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
218 },
219 {
220 .name = "exit_signal not fitting into CSIGNAL mask",
221 .flags = 0,
222 .size = 0,
223 .expected = -EINVAL,
224 .test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
225 },
226 {
227 .name = "NSIG < exit_signal < CSIG",
228 .flags = 0,
229 .size = 0,
230 .expected = -EINVAL,
231 .test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
232 },
233 {
234 .name = "Arguments sizeof(struct clone_args) + 8",
235 .flags = 0,
236 .size = sizeof(struct __clone_args) + 8,
237 .expected = 0,
238 .test_mode = CLONE3_ARGS_ALL_0,
239 },
240 {
241 .name = "Arguments sizeof(struct clone_args) + 16",
242 .flags = 0,
243 .size = sizeof(struct __clone_args) + 16,
244 .expected = -E2BIG,
245 .test_mode = CLONE3_ARGS_ALL_0,
246 },
247 {
248 .name = "Arguments sizeof(struct clone_arg) * 2",
249 .flags = 0,
250 .size = sizeof(struct __clone_args) + 16,
251 .expected = -E2BIG,
252 .test_mode = CLONE3_ARGS_ALL_0,
253 },
254 {
255 .name = "Arguments > page size",
256 .flags = 0,
257 .size_function = page_size_plus_8,
258 .expected = -E2BIG,
259 .test_mode = CLONE3_ARGS_NO_TEST,
260 },
261 {
262 .name = "CLONE_ARGS_SIZE_VER0 in a new PID NS",
263 .flags = CLONE_NEWPID,
264 .size = CLONE_ARGS_SIZE_VER0,
265 .expected = 0,
266 .test_mode = CLONE3_ARGS_NO_TEST,
267 .filter = not_root,
268 },
269 {
270 .name = "CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS",
271 .flags = CLONE_NEWPID,
272 .size = CLONE_ARGS_SIZE_VER0 - 8,
273 .expected = -EINVAL,
274 .test_mode = CLONE3_ARGS_NO_TEST,
275 },
276 {
277 .name = "sizeof(struct clone_args) + 8 in a new PID NS",
278 .flags = CLONE_NEWPID,
279 .size = sizeof(struct __clone_args) + 8,
280 .expected = 0,
281 .test_mode = CLONE3_ARGS_NO_TEST,
282 .filter = not_root,
283 },
284 {
285 .name = "Arguments > page size in a new PID NS",
286 .flags = CLONE_NEWPID,
287 .size_function = page_size_plus_8,
288 .expected = -E2BIG,
289 .test_mode = CLONE3_ARGS_NO_TEST,
290 },
291 {
292 .name = "New time NS",
293 .flags = CLONE_NEWTIME,
294 .size = 0,
295 .expected = 0,
296 .test_mode = CLONE3_ARGS_NO_TEST,
297 .filter = no_timenamespace,
298 },
299 {
300 .name = "exit signal (SIGCHLD) in flags",
301 .flags = SIGCHLD,
302 .size = 0,
303 .expected = -EINVAL,
304 .test_mode = CLONE3_ARGS_NO_TEST,
305 },
306};
307
308int main(int argc, char *argv[])
309{
310 size_t size;
311 int i;
312
313 ksft_print_header();
314 ksft_set_plan(ARRAY_SIZE(tests));
315 test_clone3_supported();
316
317 for (i = 0; i < ARRAY_SIZE(tests); i++) {
318 if (tests[i].filter && tests[i].filter()) {
319 ksft_test_result_skip(msg: "%s\n", tests[i].name);
320 continue;
321 }
322
323 if (tests[i].size_function)
324 size = tests[i].size_function();
325 else
326 size = tests[i].size;
327
328 ksft_print_msg(msg: "Running test '%s'\n", tests[i].name);
329
330 ksft_test_result(test_clone3(tests[i].flags, size,
331 tests[i].expected,
332 tests[i].test_mode),
333 "%s\n", tests[i].name);
334 }
335
336 ksft_finished();
337}
338

source code of linux/tools/testing/selftests/clone3/clone3.c