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(args: (struct __clone_args *)&args_ext, size); |
83 | if (pid < 0) { |
84 | ksft_print_msg(msg: "%s - Failed to create new process\n" , |
85 | strerror(errno)); |
86 | return -errno; |
87 | } |
88 | |
89 | if (pid == 0) { |
90 | ksft_print_msg(msg: "I am the child, my PID is %d\n" , getpid()); |
91 | _exit(EXIT_SUCCESS); |
92 | } |
93 | |
94 | ksft_print_msg(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(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(msg: "[%d] clone3() with flags says: %d expected %d\n" , |
117 | getpid(), ret, expected); |
118 | if (ret != expected) { |
119 | ksft_print_msg( |
120 | msg: "[%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(msg: "Not running as root\n" ); |
135 | return true; |
136 | } |
137 | |
138 | return false; |
139 | } |
140 | |
141 | static 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(msg: "Time namespaces are not supported\n" ); |
150 | return true; |
151 | } |
152 | |
153 | static size_t page_size_plus_8(void) |
154 | { |
155 | return getpagesize() + 8; |
156 | } |
157 | |
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 | }; |
167 | |
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, |
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 | |
308 | int 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(msg: "%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(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 | |