xref: /linux/tools/testing/selftests/clone3/clone3.c (revision 7dc0e9c7dda66bd91eeada00d90033e3eb647fc3)
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("Child returned %s\n", strerror(errno));
99 		return -errno;
100 	}
101 	if (WEXITSTATUS(status))
102 		return WEXITSTATUS(status);
103 
104 	return 0;
105 }
106 
107 static 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 
128 typedef bool (*filter_function)(void);
129 typedef size_t (*size_function)(void);
130 
131 static 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 
141 static size_t page_size_plus_8(void)
142 {
143 	return getpagesize() + 8;
144 }
145 
146 struct test {
147 	const char *name;
148 	uint64_t flags;
149 	size_t size;
150 	size_function size_function;
151 	int expected;
152 	enum test_mode test_mode;
153 	filter_function filter;
154 };
155 
156 static const struct test tests[] = {
157 	{
158 		.name = "simple clone3()",
159 		.flags = 0,
160 		.size = 0,
161 		.expected = 0,
162 		.test_mode = CLONE3_ARGS_NO_TEST,
163 	},
164 	{
165 		.name = "clone3() in a new PID_NS",
166 		.flags = CLONE_NEWPID,
167 		.size = 0,
168 		.expected = 0,
169 		.test_mode = CLONE3_ARGS_NO_TEST,
170 		.filter = not_root,
171 	},
172 	{
173 		.name = "CLONE_ARGS_SIZE_VER0",
174 		.flags = 0,
175 		.size = CLONE_ARGS_SIZE_VER0,
176 		.expected = 0,
177 		.test_mode = CLONE3_ARGS_NO_TEST,
178 	},
179 	{
180 		.name = "CLONE_ARGS_SIZE_VER0 - 8",
181 		.flags = 0,
182 		.size = CLONE_ARGS_SIZE_VER0 - 8,
183 		.expected = -EINVAL,
184 		.test_mode = CLONE3_ARGS_NO_TEST,
185 	},
186 	{
187 		.name = "sizeof(struct clone_args) + 8",
188 		.flags = 0,
189 		.size = sizeof(struct __clone_args) + 8,
190 		.expected = 0,
191 		.test_mode = CLONE3_ARGS_NO_TEST,
192 	},
193 	{
194 		.name = "exit_signal with highest 32 bits non-zero",
195 		.flags = 0,
196 		.size = 0,
197 		.expected = -EINVAL,
198 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
199 	},
200 	{
201 		.name = "negative 32-bit exit_signal",
202 		.flags = 0,
203 		.size = 0,
204 		.expected = -EINVAL,
205 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
206 	},
207 	{
208 		.name = "exit_signal not fitting into CSIGNAL mask",
209 		.flags = 0,
210 		.size = 0,
211 		.expected = -EINVAL,
212 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
213 	},
214 	{
215 		.name = "NSIG < exit_signal < CSIG",
216 		.flags = 0,
217 		.size = 0,
218 		.expected = -EINVAL,
219 		.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
220 	},
221 	{
222 		.name = "Arguments sizeof(struct clone_args) + 8",
223 		.flags = 0,
224 		.size = sizeof(struct __clone_args) + 8,
225 		.expected = 0,
226 		.test_mode = CLONE3_ARGS_ALL_0,
227 	},
228 	{
229 		.name = "Arguments sizeof(struct clone_args) + 16",
230 		.flags = 0,
231 		.size = sizeof(struct __clone_args) + 16,
232 		.expected = -E2BIG,
233 		.test_mode = CLONE3_ARGS_ALL_0,
234 	},
235 	{
236 		.name = "Arguments sizeof(struct clone_arg) * 2",
237 		.flags = 0,
238 		.size = sizeof(struct __clone_args) + 16,
239 		.expected = -E2BIG,
240 		.test_mode = CLONE3_ARGS_ALL_0,
241 	},
242 	{
243 		.name = "Arguments > page size",
244 		.flags = 0,
245 		.size_function = page_size_plus_8,
246 		.expected = -E2BIG,
247 		.test_mode = CLONE3_ARGS_NO_TEST,
248 	},
249 	{
250 		.name = "CLONE_ARGS_SIZE_VER0 in a new PID NS",
251 		.flags = CLONE_NEWPID,
252 		.size = CLONE_ARGS_SIZE_VER0,
253 		.expected = 0,
254 		.test_mode = CLONE3_ARGS_NO_TEST,
255 		.filter = not_root,
256 	},
257 	{
258 		.name = "CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS",
259 		.flags = CLONE_NEWPID,
260 		.size = CLONE_ARGS_SIZE_VER0 - 8,
261 		.expected = -EINVAL,
262 		.test_mode = CLONE3_ARGS_NO_TEST,
263 	},
264 	{
265 		.name = "sizeof(struct clone_args) + 8 in a new PID NS",
266 		.flags = CLONE_NEWPID,
267 		.size = sizeof(struct __clone_args) + 8,
268 		.expected = 0,
269 		.test_mode = CLONE3_ARGS_NO_TEST,
270 		.filter = not_root,
271 	},
272 	{
273 		.name = "Arguments > page size in a new PID NS",
274 		.flags = CLONE_NEWPID,
275 		.size_function = page_size_plus_8,
276 		.expected = -E2BIG,
277 		.test_mode = CLONE3_ARGS_NO_TEST,
278 	},
279 	{
280 		.name = "New time NS",
281 		.flags = CLONE_NEWTIME,
282 		.size = 0,
283 		.expected = 0,
284 		.test_mode = CLONE3_ARGS_NO_TEST,
285 	},
286 	{
287 		.name = "exit signal (SIGCHLD) in flags",
288 		.flags = SIGCHLD,
289 		.size = 0,
290 		.expected = -EINVAL,
291 		.test_mode = CLONE3_ARGS_NO_TEST,
292 	},
293 };
294 
295 int main(int argc, char *argv[])
296 {
297 	size_t size;
298 	int i;
299 
300 	ksft_print_header();
301 	ksft_set_plan(ARRAY_SIZE(tests));
302 	test_clone3_supported();
303 
304 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
305 		if (tests[i].filter && tests[i].filter()) {
306 			ksft_test_result_skip("%s\n", tests[i].name);
307 			continue;
308 		}
309 
310 		if (tests[i].size_function)
311 			size = tests[i].size_function();
312 		else
313 			size = tests[i].size;
314 
315 		ksft_print_msg("Running test '%s'\n", tests[i].name);
316 
317 		ksft_test_result(test_clone3(tests[i].flags, size,
318 					     tests[i].expected,
319 					     tests[i].test_mode),
320 				 "%s\n", tests[i].name);
321 	}
322 
323 	ksft_finished();
324 }
325