File Index
Files and directories
Sign in
FILE

clone3.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/clone3/clone3.c C
  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("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
112static 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
133typedef bool (*filter_function)(void);
134typedef size_t (*size_function)(void);
135
136static 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
146static 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
158static size_t page_size_plus_8(void)
159{
160	return getpagesize() + 8;
161}
162
163struct 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
173static 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
313int 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}
~/ Read-only file preview.