1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <sched.h>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <sys/mman.h>
10 #include <sys/types.h>
11 #include <sys/wait.h>
12 #include <unistd.h>
13
14 #include "kselftest_harness.h"
15
16 #define STACK_SIZE (1024 * 1024)
17
18 struct pidns_init_args {
19 int ready_read;
20 int ready_write;
21 int release_read;
22 int release_write;
23 };
24
pidns_init(void * data)25 static int pidns_init(void *data)
26 {
27 struct pidns_init_args *args = data;
28 char release;
29 int error = 0;
30
31 close(args->ready_read);
32 close(args->release_write);
33 if (unshare(CLONE_NEWPID))
34 error = errno;
35 if (write(args->ready_write, &error, sizeof(error)) != sizeof(error))
36 return 1;
37 close(args->ready_write);
38 if (error)
39 return 0;
40 if (read(args->release_read, &release, sizeof(release)) != sizeof(release))
41 return 1;
42 close(args->release_read);
43 return 0;
44 }
45
create_unborn_pidns(void)46 static int create_unborn_pidns(void)
47 {
48 struct pidns_init_args args;
49 char path[64], release = 0;
50 void *stack;
51 pid_t child;
52 int ready[2], release_pipe[2], error, status, nsfd = -1;
53 ssize_t n;
54
55 if (pipe(ready))
56 return -1;
57 if (pipe(release_pipe)) {
58 error = errno;
59 close(ready[0]);
60 close(ready[1]);
61 errno = error;
62 return -1;
63 }
64
65 stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
66 MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
67 if (stack == MAP_FAILED) {
68 error = errno;
69 close(ready[0]);
70 close(ready[1]);
71 close(release_pipe[0]);
72 close(release_pipe[1]);
73 errno = error;
74 return -1;
75 }
76
77 args = (struct pidns_init_args) {
78 .ready_read = ready[0],
79 .ready_write = ready[1],
80 .release_read = release_pipe[0],
81 .release_write = release_pipe[1],
82 };
83 child = clone(pidns_init, stack + STACK_SIZE, CLONE_NEWPID | SIGCHLD,
84 &args);
85 if (child < 0)
86 goto err_munmap;
87
88 close(ready[1]);
89 close(release_pipe[0]);
90 n = read(ready[0], &error, sizeof(error));
91 if (n != sizeof(error)) {
92 error = n < 0 ? errno : EIO;
93 goto release_child;
94 }
95 if (error)
96 goto release_child;
97
98 snprintf(path, sizeof(path), "/proc/%d/ns/pid_for_children", child);
99 nsfd = open(path, O_RDONLY | O_CLOEXEC);
100 if (nsfd < 0) {
101 error = errno;
102 goto release_child;
103 }
104
105 release_child:
106 write(release_pipe[1], &release, sizeof(release));
107 close(release_pipe[1]);
108 close(ready[0]);
109 if (waitpid(child, &status, 0) != child || !WIFEXITED(status) ||
110 WEXITSTATUS(status)) {
111 if (!error)
112 error = ECHILD;
113 if (nsfd >= 0)
114 close(nsfd);
115 nsfd = -1;
116 }
117 munmap(stack, STACK_SIZE);
118 if (error) {
119 errno = error;
120 return -1;
121 }
122 return nsfd;
123
124 err_munmap:
125 error = errno;
126 munmap(stack, STACK_SIZE);
127 close(ready[0]);
128 close(ready[1]);
129 close(release_pipe[0]);
130 close(release_pipe[1]);
131 errno = error;
132 return -1;
133 }
134
TEST(pidns_dead_ancestor_rejects_fork)135 TEST(pidns_dead_ancestor_rejects_fork)
136 {
137 int original_fd, target_fd, fork_errno, ret;
138 pid_t child;
139
140 target_fd = create_unborn_pidns();
141 if (target_fd < 0) {
142 if (errno == ENOENT)
143 SKIP(return, "PID namespace handle unavailable through procfs\n");
144 if (errno == EPERM || errno == EACCES || errno == EINVAL)
145 SKIP(return, "PID namespace creation requires privileges\n");
146 ASSERT_GE(target_fd, 0);
147 }
148
149 original_fd = open("/proc/self/ns/pid_for_children", O_RDONLY | O_CLOEXEC);
150 ASSERT_GE(original_fd, 0);
151 ret = setns(target_fd, CLONE_NEWPID);
152 if (ret) {
153 if (errno == EPERM || errno == EACCES)
154 SKIP(return, "setns into PID namespace requires privileges\n");
155 }
156 ASSERT_EQ(0, ret);
157
158 child = fork();
159 fork_errno = errno;
160 if (child == 0)
161 _exit(0);
162
163 if (child > 0)
164 ASSERT_EQ(child, waitpid(child, NULL, 0));
165 ret = setns(original_fd, CLONE_NEWPID);
166 close(original_fd);
167 close(target_fd);
168 ASSERT_EQ(0, ret);
169
170 ASSERT_EQ(-1, child);
171 ASSERT_EQ(ENOMEM, fork_errno);
172 }
173
174 TEST_HARNESS_MAIN
175