Commit | Line | Data |
---|---|---|
17a81069 AR |
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> | |
34dce23f | 10 | #include <stdbool.h> |
17a81069 AR |
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" | |
41585bbe | 22 | #include "clone3_selftests.h" |
17a81069 | 23 | |
17a81069 AR |
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 | ||
17a81069 AR |
33 | static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode) |
34 | { | |
e953aeaa | 35 | struct __clone_args args = { |
17a81069 AR |
36 | .flags = flags, |
37 | .exit_signal = SIGCHLD, | |
38 | }; | |
39 | ||
40 | struct clone_args_extended { | |
e953aeaa | 41 | struct __clone_args args; |
17a81069 AR |
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)); | |
e953aeaa | 49 | if (size > sizeof(struct __clone_args)) |
17a81069 AR |
50 | args_ext.excess_space[1] = 1; |
51 | ||
52 | if (size == 0) | |
e953aeaa | 53 | size = sizeof(struct __clone_args); |
17a81069 AR |
54 | |
55 | switch (test_mode) { | |
a531b0c2 AR |
56 | case CLONE3_ARGS_NO_TEST: |
57 | /* | |
58 | * Uses default 'flags' and 'SIGCHLD' | |
59 | * assignment. | |
60 | */ | |
61 | break; | |
17a81069 AR |
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 | ||
e953aeaa | 80 | memcpy(&args_ext.args, &args, sizeof(struct __clone_args)); |
17a81069 | 81 | |
e953aeaa | 82 | pid = sys_clone3((struct __clone_args *)&args_ext, size); |
17a81069 AR |
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 | ||
34dce23f MB |
107 | static bool test_clone3(uint64_t flags, size_t size, int expected, |
108 | enum test_mode test_mode) | |
17a81069 AR |
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); | |
34dce23f MB |
118 | if (ret != expected) { |
119 | ksft_print_msg( | |
17a81069 AR |
120 | "[%d] Result (%d) is different than expected (%d)\n", |
121 | getpid(), ret, expected); | |
34dce23f MB |
122 | return false; |
123 | } | |
17a81069 | 124 | |
34dce23f MB |
125 | return true; |
126 | } | |
17a81069 | 127 | |
34dce23f MB |
128 | typedef bool (*filter_function)(void); |
129 | typedef size_t (*size_function)(void); | |
17a81069 | 130 | |
34dce23f MB |
131 | static bool not_root(void) |
132 | { | |
133 | if (getuid() != 0) { | |
134 | ksft_print_msg("Not running as root\n"); | |
135 | return true; | |
136 | } | |
17a81069 | 137 | |
34dce23f MB |
138 | return false; |
139 | } | |
17a81069 | 140 | |
ecae0bd5 LT |
141 | static bool no_timenamespace(void) |
142 | { | |
143 | if (not_root()) | |
144 | return true; | |
17a81069 | 145 | |
ecae0bd5 LT |
146 | if (!access("/proc/self/ns/time", F_OK)) |
147 | return false; | |
17a81069 | 148 | |
ecae0bd5 LT |
149 | ksft_print_msg("Time namespaces are not supported\n"); |
150 | return true; | |
151 | } | |
17a81069 | 152 | |
34dce23f MB |
153 | static size_t page_size_plus_8(void) |
154 | { | |
155 | return getpagesize() + 8; | |
156 | } | |
17a81069 | 157 | |
34dce23f MB |
158 | struct 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 | }; | |
17a81069 | 167 | |
34dce23f MB |
168 | static 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, | |
ecae0bd5 | 297 | .filter = no_timenamespace, |
34dce23f MB |
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 | }; | |
17a81069 | 307 | |
34dce23f MB |
308 | int main(int argc, char *argv[]) |
309 | { | |
310 | size_t size; | |
311 | int i; | |
17a81069 | 312 | |
34dce23f MB |
313 | ksft_print_header(); |
314 | ksft_set_plan(ARRAY_SIZE(tests)); | |
315 | test_clone3_supported(); | |
17a81069 | 316 | |
34dce23f MB |
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 | } | |
17a81069 | 322 | |
34dce23f MB |
323 | if (tests[i].size_function) |
324 | size = tests[i].size_function(); | |
325 | else | |
326 | size = tests[i].size; | |
17a81069 | 327 | |
34dce23f | 328 | ksft_print_msg("Running test '%s'\n", tests[i].name); |
515bddf0 | 329 | |
34dce23f MB |
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 | } | |
c4f461a1 | 335 | |
d95debbd | 336 | ksft_finished(); |
17a81069 | 337 | } |