]> Git Repo - J-linux.git/blob - tools/testing/selftests/clone3/clone3_set_tid.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / clone3 / clone3_set_tid.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4  * Based on Christian Brauner's clone3() example.
5  * These tests are assuming to be running in the host's
6  * PID namespace.
7  */
8
9 #define _GNU_SOURCE
10 #include <errno.h>
11 #include <linux/types.h>
12 #include <linux/sched.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdbool.h>
16 #include <sys/syscall.h>
17 #include <sys/types.h>
18 #include <sys/un.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21 #include <sched.h>
22
23 #include "../kselftest.h"
24 #include "clone3_selftests.h"
25
26 #define MAX_PID_NS_LEVEL 32
27
28 static int pipe_1[2];
29 static int pipe_2[2];
30
31 static void child_exit(int ret)
32 {
33         fflush(stdout);
34         fflush(stderr);
35         _exit(ret);
36 }
37
38 static int call_clone3_set_tid(pid_t *set_tid,
39                                size_t set_tid_size,
40                                int flags,
41                                int expected_pid,
42                                bool wait_for_it)
43 {
44         int status;
45         pid_t pid = -1;
46
47         struct __clone_args args = {
48                 .flags = flags,
49                 .exit_signal = SIGCHLD,
50                 .set_tid = ptr_to_u64(set_tid),
51                 .set_tid_size = set_tid_size,
52         };
53
54         pid = sys_clone3(&args, sizeof(args));
55         if (pid < 0) {
56                 ksft_print_msg("%s - Failed to create new process\n",
57                                strerror(errno));
58                 return -errno;
59         }
60
61         if (pid == 0) {
62                 int ret;
63                 char tmp = 0;
64                 int exit_code = EXIT_SUCCESS;
65
66                 ksft_print_msg("I am the child, my PID is %d (expected %d)\n",
67                                getpid(), set_tid[0]);
68                 if (wait_for_it) {
69                         ksft_print_msg("[%d] Child is ready and waiting\n",
70                                        getpid());
71
72                         /* Signal the parent that the child is ready */
73                         close(pipe_1[0]);
74                         ret = write(pipe_1[1], &tmp, 1);
75                         if (ret != 1) {
76                                 ksft_print_msg(
77                                         "Writing to pipe returned %d", ret);
78                                 exit_code = EXIT_FAILURE;
79                         }
80                         close(pipe_1[1]);
81                         close(pipe_2[1]);
82                         ret = read(pipe_2[0], &tmp, 1);
83                         if (ret != 1) {
84                                 ksft_print_msg(
85                                         "Reading from pipe returned %d", ret);
86                                 exit_code = EXIT_FAILURE;
87                         }
88                         close(pipe_2[0]);
89                 }
90
91                 if (set_tid[0] != getpid())
92                         child_exit(EXIT_FAILURE);
93                 child_exit(exit_code);
94         }
95
96         if (expected_pid == 0 || expected_pid == pid) {
97                 ksft_print_msg("I am the parent (%d). My child's pid is %d\n",
98                                getpid(), pid);
99         } else {
100                 ksft_print_msg(
101                         "Expected child pid %d does not match actual pid %d\n",
102                         expected_pid, pid);
103                 return -1;
104         }
105
106         if (waitpid(pid, &status, 0) < 0) {
107                 ksft_print_msg("Child returned %s\n", strerror(errno));
108                 return -errno;
109         }
110
111         if (!WIFEXITED(status))
112                 return -1;
113
114         return WEXITSTATUS(status);
115 }
116
117 static void test_clone3_set_tid(const char *desc,
118                                 pid_t *set_tid,
119                                 size_t set_tid_size,
120                                 int flags,
121                                 int expected,
122                                 int expected_pid,
123                                 bool wait_for_it)
124 {
125         int ret;
126
127         ksft_print_msg(
128                 "[%d] Trying clone3() with CLONE_SET_TID to %d and 0x%x\n",
129                 getpid(), set_tid[0], flags);
130         ret = call_clone3_set_tid(set_tid, set_tid_size, flags, expected_pid,
131                                   wait_for_it);
132         ksft_print_msg(
133                 "[%d] clone3() with CLONE_SET_TID %d says: %d - expected %d\n",
134                 getpid(), set_tid[0], ret, expected);
135
136         ksft_test_result(ret == expected, "%s with %zu TIDs and flags 0x%x\n",
137                          desc, set_tid_size, flags);
138 }
139
140 int main(int argc, char *argv[])
141 {
142         FILE *f;
143         char buf;
144         char *line;
145         int status;
146         int ret = -1;
147         size_t len = 0;
148         int pid_max = 0;
149         uid_t uid = getuid();
150         char proc_path[100] = {0};
151         pid_t pid, ns1, ns2, ns3, ns_pid;
152         pid_t set_tid[MAX_PID_NS_LEVEL * 2];
153
154         ksft_print_header();
155         ksft_set_plan(29);
156         test_clone3_supported();
157
158         if (pipe(pipe_1) < 0 || pipe(pipe_2) < 0)
159                 ksft_exit_fail_msg("pipe() failed\n");
160
161         f = fopen("/proc/sys/kernel/pid_max", "r");
162         if (f == NULL)
163                 ksft_exit_fail_msg(
164                         "%s - Could not open /proc/sys/kernel/pid_max\n",
165                         strerror(errno));
166         fscanf(f, "%d", &pid_max);
167         fclose(f);
168         ksft_print_msg("/proc/sys/kernel/pid_max %d\n", pid_max);
169
170         /* Try invalid settings */
171         memset(&set_tid, 0, sizeof(set_tid));
172         test_clone3_set_tid("invalid size, 0 TID",
173                             set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0);
174
175         test_clone3_set_tid("invalid size, 0 TID",
176                             set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0);
177
178         test_clone3_set_tid("invalid size, 0 TID",
179                             set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0,
180                             -EINVAL, 0, 0);
181
182         test_clone3_set_tid("invalid size, 0 TID",
183                             set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0);
184
185         /*
186          * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1
187          * nested PID namespace.
188          */
189         test_clone3_set_tid("invalid size, 0 TID",
190                             set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0);
191
192         memset(&set_tid, 0xff, sizeof(set_tid));
193         test_clone3_set_tid("invalid size, TID all 1s",
194                             set_tid, MAX_PID_NS_LEVEL + 1, 0, -EINVAL, 0, 0);
195
196         test_clone3_set_tid("invalid size, TID all 1s",
197                             set_tid, MAX_PID_NS_LEVEL * 2, 0, -EINVAL, 0, 0);
198
199         test_clone3_set_tid("invalid size, TID all 1s",
200                             set_tid, MAX_PID_NS_LEVEL * 2 + 1, 0,
201                             -EINVAL, 0, 0);
202
203         test_clone3_set_tid("invalid size, TID all 1s",
204                             set_tid, MAX_PID_NS_LEVEL * 42, 0, -EINVAL, 0, 0);
205
206         /*
207          * This can actually work if this test running in a MAX_PID_NS_LEVEL - 1
208          * nested PID namespace.
209          */
210         test_clone3_set_tid("invalid size, TID all 1s",
211                             set_tid, MAX_PID_NS_LEVEL - 1, 0, -EINVAL, 0, 0);
212
213         memset(&set_tid, 0, sizeof(set_tid));
214         /* Try with an invalid PID */
215         set_tid[0] = 0;
216         test_clone3_set_tid("valid size, 0 TID",
217                             set_tid, 1, 0, -EINVAL, 0, 0);
218
219         set_tid[0] = -1;
220         test_clone3_set_tid("valid size, -1 TID",
221                             set_tid, 1, 0, -EINVAL, 0, 0);
222
223         /* Claim that the set_tid array actually contains 2 elements. */
224         test_clone3_set_tid("2 TIDs, -1 and 0",
225                             set_tid, 2, 0, -EINVAL, 0, 0);
226
227         /* Try it in a new PID namespace */
228         if (uid == 0)
229                 test_clone3_set_tid("valid size, -1 TID",
230                                     set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);
231         else
232                 ksft_test_result_skip("Clone3() with set_tid requires root\n");
233
234         /* Try with a valid PID (1) this should return -EEXIST. */
235         set_tid[0] = 1;
236         if (uid == 0)
237                 test_clone3_set_tid("duplicate PID 1",
238                                     set_tid, 1, 0, -EEXIST, 0, 0);
239         else
240                 ksft_test_result_skip("Clone3() with set_tid requires root\n");
241
242         /* Try it in a new PID namespace */
243         if (uid == 0)
244                 test_clone3_set_tid("duplicate PID 1",
245                                     set_tid, 1, CLONE_NEWPID, 0, 0, 0);
246         else
247                 ksft_test_result_skip("Clone3() with set_tid requires root\n");
248
249         /* pid_max should fail everywhere */
250         set_tid[0] = pid_max;
251         test_clone3_set_tid("set TID to maximum",
252                             set_tid, 1, 0, -EINVAL, 0, 0);
253
254         if (uid == 0)
255                 test_clone3_set_tid("set TID to maximum",
256                                     set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);
257         else
258                 ksft_test_result_skip("Clone3() with set_tid requires root\n");
259
260         if (uid != 0) {
261                 /*
262                  * All remaining tests require root. Tell the framework
263                  * that all those tests are skipped as non-root.
264                  */
265                 ksft_cnt.ksft_xskip += ksft_plan - ksft_test_num();
266                 goto out;
267         }
268
269         /* Find the current active PID */
270         pid = fork();
271         if (pid == 0) {
272                 ksft_print_msg("Child has PID %d\n", getpid());
273                 child_exit(EXIT_SUCCESS);
274         }
275         if (waitpid(pid, &status, 0) < 0)
276                 ksft_exit_fail_msg("Waiting for child %d failed", pid);
277
278         /* After the child has finished, its PID should be free. */
279         set_tid[0] = pid;
280         test_clone3_set_tid("reallocate child TID",
281                             set_tid, 1, 0, 0, 0, 0);
282
283         /* This should fail as there is no PID 1 in that namespace */
284         test_clone3_set_tid("duplicate child TID",
285                             set_tid, 1, CLONE_NEWPID, -EINVAL, 0, 0);
286
287         /*
288          * Creating a process with PID 1 in the newly created most nested
289          * PID namespace and PID 'pid' in the parent PID namespace. This
290          * needs to work.
291          */
292         set_tid[0] = 1;
293         set_tid[1] = pid;
294         test_clone3_set_tid("create PID 1 in new NS",
295                             set_tid, 2, CLONE_NEWPID, 0, pid, 0);
296
297         ksft_print_msg("unshare PID namespace\n");
298         if (unshare(CLONE_NEWPID) == -1)
299                 ksft_exit_fail_msg("unshare(CLONE_NEWPID) failed: %s\n",
300                                 strerror(errno));
301
302         set_tid[0] = pid;
303
304         /* This should fail as there is no PID 1 in that namespace */
305         test_clone3_set_tid("duplicate PID 1",
306                             set_tid, 1, 0, -EINVAL, 0, 0);
307
308         /* Let's create a PID 1 */
309         ns_pid = fork();
310         if (ns_pid == 0) {
311                 /*
312                  * This and the next test cases check that all pid-s are
313                  * released on error paths.
314                  */
315                 set_tid[0] = 43;
316                 set_tid[1] = -1;
317                 test_clone3_set_tid("check leak on invalid TID -1",
318                                     set_tid, 2, 0, -EINVAL, 0, 0);
319
320                 set_tid[0] = 43;
321                 set_tid[1] = pid;
322                 test_clone3_set_tid("check leak on invalid specific TID",
323                                     set_tid, 2, 0, 0, 43, 0);
324
325                 ksft_print_msg("Child in PID namespace has PID %d\n", getpid());
326                 set_tid[0] = 2;
327                 test_clone3_set_tid("create PID 2 in child NS",
328                                     set_tid, 1, 0, 0, 2, 0);
329
330                 set_tid[0] = 1;
331                 set_tid[1] = -1;
332                 set_tid[2] = pid;
333                 /* This should fail as there is invalid PID at level '1'. */
334                 test_clone3_set_tid("fail due to invalid TID at level 1",
335                                     set_tid, 3, CLONE_NEWPID, -EINVAL, 0, 0);
336
337                 set_tid[0] = 1;
338                 set_tid[1] = 42;
339                 set_tid[2] = pid;
340                 /*
341                  * This should fail as there are not enough active PID
342                  * namespaces. Again assuming this is running in the host's
343                  * PID namespace. Not yet nested.
344                  */
345                 test_clone3_set_tid("fail due to too few active PID NSs",
346                                     set_tid, 4, CLONE_NEWPID, -EINVAL, 0, 0);
347
348                 /*
349                  * This should work and from the parent we should see
350                  * something like 'NSpid:       pid     42      1'.
351                  */
352                 test_clone3_set_tid("verify that we have 3 PID NSs",
353                                     set_tid, 3, CLONE_NEWPID, 0, 42, true);
354
355                 child_exit(ksft_cnt.ksft_fail);
356         }
357
358         close(pipe_1[1]);
359         close(pipe_2[0]);
360         while (read(pipe_1[0], &buf, 1) > 0) {
361                 ksft_print_msg("[%d] Child is ready and waiting\n", getpid());
362                 break;
363         }
364
365         snprintf(proc_path, sizeof(proc_path), "/proc/%d/status", pid);
366         f = fopen(proc_path, "r");
367         if (f == NULL)
368                 ksft_exit_fail_msg(
369                         "%s - Could not open %s\n",
370                         strerror(errno), proc_path);
371
372         while (getline(&line, &len, f) != -1) {
373                 if (strstr(line, "NSpid")) {
374                         int i;
375
376                         /* Verify that all generated PIDs are as expected. */
377                         i = sscanf(line, "NSpid:\t%d\t%d\t%d",
378                                    &ns3, &ns2, &ns1);
379                         if (i != 3) {
380                                 ksft_print_msg(
381                                         "Unexpected 'NSPid:' entry: %s",
382                                         line);
383                                 ns1 = ns2 = ns3 = 0;
384                         }
385                         break;
386                 }
387         }
388         fclose(f);
389         free(line);
390         close(pipe_2[0]);
391
392         /* Tell the clone3()'d child to finish. */
393         write(pipe_2[1], &buf, 1);
394         close(pipe_2[1]);
395
396         if (waitpid(ns_pid, &status, 0) < 0) {
397                 ksft_print_msg("Child returned %s\n", strerror(errno));
398                 ret = -errno;
399                 goto out;
400         }
401
402         if (!WIFEXITED(status))
403                 ksft_test_result_fail("Child error\n");
404
405         ksft_cnt.ksft_pass += 6 - (ksft_cnt.ksft_fail - WEXITSTATUS(status));
406         ksft_cnt.ksft_fail = WEXITSTATUS(status);
407
408         ksft_print_msg("Expecting PIDs %d, 42, 1\n", pid);
409         ksft_print_msg("Have PIDs in namespaces: %d, %d, %d\n", ns3, ns2, ns1);
410         ksft_test_result(ns3 == pid && ns2 == 42 && ns1 == 1,
411                          "PIDs in all namespaces as expected\n");
412 out:
413         ret = 0;
414
415         if (ret)
416                 ksft_exit_fail();
417         ksft_exit_pass();
418 }
This page took 0.05279 seconds and 4 git commands to generate.