xref: /linux/tools/testing/selftests/clone3/clone3.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 
24 enum 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 
33 static 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("waitpid() returned %s\n", strerror(errno));
99 		return -errno;
100 	}
101 	if (!WIFEXITED(status)) {
102 		ksft_print_msg("Child did not exit normally, status 0x%x\n",
103 			       status);
104 		return EXIT_FAILURE;
105 	}
106 	if (WEXITSTATUS(status))
107 		return WEXITSTATUS(status);
108 
109 	return 0;
110 }
111 
112 static bool test_clone3(uint64_t flags, size_t size, int expected,
113 			enum test_mode test_mode)
114 {
115 	int ret;
116 
117 	ksft_print_msg(
118 		"[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",
119 		getpid(), flags, size);
120 	ret = call_clone3(flags, size, test_mode);
121 	ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",
122 			getpid(), ret, expected);
123 	if (ret != expected) {
124 		ksft_print_msg(
125 			"[%d] Result (%d) is different than expected (%d)\n",
126 			getpid(), ret, expected);
127 		return false;
128 	}
129 
130 	return true;
131 }
132 
133 typedef bool (*filter_function)(void);
134 typedef size_t (*size_function)(void);
135 
136 static bool not_root(void)
137 {
138 	if (getuid() != 0) {
139 		ksft_print_msg("Not running as root\n");
140 		return true;
141 	}
142 
143 	return false;
144 }
145 
146 static bool no_timenamespace(void)
147 {
148 	if (not_root())
149 		return true;
150 
151 	if (!access("/proc/self/ns/time", F_OK))
152 		return false;
153 
154 	ksft_print_msg("Time namespaces are not supported\n");
155 	return true;
156 }
157 
158 static size_t page_size_plus_8(void)
159 {
160 	return getpagesize() + 8;
161 }
162 
163 struct test {
164 	const char *name;
165 	uint64_t flags;
166 	size_t size;
167 	size_function size_function;
168 	int expected;
169 	enum test_mode test_mode;
170 	filter_function filter;
171 };
172 
173 static const struct test tests[] = {
174 	{
175 		.name = "simple clone3()",
176 		.flags = 0,
177 		.size = 0,
178 		.expected = 0,
179 		.test_mode = CLONE3_ARGS_NO_TEST,
180 	},
181 	{
182 		.name = "clone3() in a new PID_NS",
183 		.flags = CLONE_NEWPID,
184 		.size = 0,
185 		.expected = 0,
186 		.test_mode = CLONE3_ARGS_NO_TEST,
187 		.filter = not_root,
188 	},
189 	{
190 		.name = "CLONE_ARGS_SIZE_VER0",
191 		.flags = 0,
192 		.size = CLONE_ARGS_SIZE_VER0,
193 		.expected = 0,
194 		.test_mode = CLONE3_ARGS_NO_TEST,
195 	},
196 	{
197 		.name = "CLONE_ARGS_SIZE_VER0 - 8",
198 		.flags = 0,
199 		.size = CLONE_ARGS_SIZE_VER0 - 8,
200 		.expected = -EINVAL,
201 		.test_mode = CLONE3_ARGS_NO_TEST,
202 	},
203 	{
204 		.name = "sizeof(struct clone_args) + 8",
205 		.flags = 0,
206 		.size = sizeof(struct __clone_args) + 8,
207 		.expected = 0,
208 		.test_mode = CLONE3_ARGS_NO_TEST,
209 	},
210 	{
211 		.name = "exit_signal with highest 32 bits non-zero",
212 		.flags = 0,
213 		.size = 0,
214 		.expected = -EINVAL,
215 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
216 	},
217 	{
218 		.name = "negative 32-bit exit_signal",
219 		.flags = 0,
220 		.size = 0,
221 		.expected = -EINVAL,
222 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
223 	},
224 	{
225 		.name = "exit_signal not fitting into CSIGNAL mask",
226 		.flags = 0,
227 		.size = 0,
228 		.expected = -EINVAL,
229 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
230 	},
231 	{
232 		.name = "NSIG < exit_signal < CSIG",
233 		.flags = 0,
234 		.size = 0,
235 		.expected = -EINVAL,
236 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
237 	},
238 	{
239 		.name = "Arguments sizeof(struct clone_args) + 8",
240 		.flags = 0,
241 		.size = sizeof(struct __clone_args) + 8,
242 		.expected = 0,
243 		.test_mode = CLONE3_ARGS_ALL_0,
244 	},
245 	{
246 		.name = "Arguments sizeof(struct clone_args) + 16",
247 		.flags = 0,
248 		.size = sizeof(struct __clone_args) + 16,
249 		.expected = -E2BIG,
250 		.test_mode = CLONE3_ARGS_ALL_0,
251 	},
252 	{
253 		.name = "Arguments sizeof(struct clone_arg) * 2",
254 		.flags = 0,
255 		.size = sizeof(struct __clone_args) + 16,
256 		.expected = -E2BIG,
257 		.test_mode = CLONE3_ARGS_ALL_0,
258 	},
259 	{
260 		.name = "Arguments > page size",
261 		.flags = 0,
262 		.size_function = page_size_plus_8,
263 		.expected = -E2BIG,
264 		.test_mode = CLONE3_ARGS_NO_TEST,
265 	},
266 	{
267 		.name = "CLONE_ARGS_SIZE_VER0 in a new PID NS",
268 		.flags = CLONE_NEWPID,
269 		.size = CLONE_ARGS_SIZE_VER0,
270 		.expected = 0,
271 		.test_mode = CLONE3_ARGS_NO_TEST,
272 		.filter = not_root,
273 	},
274 	{
275 		.name = "CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS",
276 		.flags = CLONE_NEWPID,
277 		.size = CLONE_ARGS_SIZE_VER0 - 8,
278 		.expected = -EINVAL,
279 		.test_mode = CLONE3_ARGS_NO_TEST,
280 	},
281 	{
282 		.name = "sizeof(struct clone_args) + 8 in a new PID NS",
283 		.flags = CLONE_NEWPID,
284 		.size = sizeof(struct __clone_args) + 8,
285 		.expected = 0,
286 		.test_mode = CLONE3_ARGS_NO_TEST,
287 		.filter = not_root,
288 	},
289 	{
290 		.name = "Arguments > page size in a new PID NS",
291 		.flags = CLONE_NEWPID,
292 		.size_function = page_size_plus_8,
293 		.expected = -E2BIG,
294 		.test_mode = CLONE3_ARGS_NO_TEST,
295 	},
296 	{
297 		.name = "New time NS",
298 		.flags = CLONE_NEWTIME,
299 		.size = 0,
300 		.expected = 0,
301 		.test_mode = CLONE3_ARGS_NO_TEST,
302 		.filter = no_timenamespace,
303 	},
304 	{
305 		.name = "exit signal (SIGCHLD) in flags",
306 		.flags = SIGCHLD,
307 		.size = 0,
308 		.expected = -EINVAL,
309 		.test_mode = CLONE3_ARGS_NO_TEST,
310 	},
311 };
312 
313 int main(int argc, char *argv[])
314 {
315 	size_t size;
316 	int i;
317 
318 	ksft_print_header();
319 	ksft_set_plan(ARRAY_SIZE(tests));
320 	test_clone3_supported();
321 
322 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
323 		if (tests[i].filter && tests[i].filter()) {
324 			ksft_test_result_skip("%s\n", tests[i].name);
325 			continue;
326 		}
327 
328 		if (tests[i].size_function)
329 			size = tests[i].size_function();
330 		else
331 			size = tests[i].size;
332 
333 		ksft_print_msg("Running test '%s'\n", tests[i].name);
334 
335 		ksft_test_result(test_clone3(tests[i].flags, size,
336 					     tests[i].expected,
337 					     tests[i].test_mode),
338 				 "%s\n", tests[i].name);
339 	}
340 
341 	ksft_finished();
342 }
343