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((struct __clone_args *)&args_ext, size);
83	if (pid < 0) {
84		ksft_print_msg("%s - Failed to create new process\n",
85				strerror(errno));
86		return -errno;
87	}
88
89	if (pid == 0) {
90		ksft_print_msg("I am the child, my PID is %d\n", getpid());
91		_exit(EXIT_SUCCESS);
92	}
93
94	ksft_print_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("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("[%d] clone3() with flags says: %d expected %d\n",
117			getpid(), ret, expected);
118	if (ret != expected) {
119		ksft_print_msg(
120			"[%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("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("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("%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("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