1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #define _GNU_SOURCE |
4 | #include <assert.h> |
5 | #include <errno.h> |
6 | #include <fcntl.h> |
7 | #include <linux/types.h> |
8 | #include <sched.h> |
9 | #include <signal.h> |
10 | #include <stdio.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | #include <syscall.h> |
14 | #include <sys/wait.h> |
15 | #include <sys/mman.h> |
16 | |
17 | #include "pidfd.h" |
18 | #include "../kselftest.h" |
19 | |
20 | struct error { |
21 | int code; |
22 | char msg[512]; |
23 | }; |
24 | |
25 | static int error_set(struct error *err, int code, const char *fmt, ...) |
26 | { |
27 | va_list args; |
28 | int r; |
29 | |
30 | if (code == PIDFD_PASS || !err || err->code != PIDFD_PASS) |
31 | return code; |
32 | |
33 | err->code = code; |
34 | va_start(args, fmt); |
35 | r = vsnprintf(err->msg, sizeof(err->msg), fmt, args); |
36 | assert((size_t)r < sizeof(err->msg)); |
37 | va_end(args); |
38 | |
39 | return code; |
40 | } |
41 | |
42 | static void error_report(struct error *err, const char *test_name) |
43 | { |
44 | switch (err->code) { |
45 | case PIDFD_ERROR: |
46 | ksft_exit_fail_msg(msg: "%s test: Fatal: %s\n" , test_name, err->msg); |
47 | break; |
48 | |
49 | case PIDFD_FAIL: |
50 | /* will be: not ok %d # error %s test: %s */ |
51 | ksft_test_result_error(msg: "%s test: %s\n" , test_name, err->msg); |
52 | break; |
53 | |
54 | case PIDFD_SKIP: |
55 | /* will be: not ok %d # SKIP %s test: %s */ |
56 | ksft_test_result_skip(msg: "%s test: %s\n" , test_name, err->msg); |
57 | break; |
58 | |
59 | case PIDFD_XFAIL: |
60 | ksft_test_result_pass(msg: "%s test: Expected failure: %s\n" , |
61 | test_name, err->msg); |
62 | break; |
63 | |
64 | case PIDFD_PASS: |
65 | ksft_test_result_pass(msg: "%s test: Passed\n" , test_name); |
66 | break; |
67 | |
68 | default: |
69 | ksft_exit_fail_msg(msg: "%s test: Unknown code: %d %s\n" , |
70 | test_name, err->code, err->msg); |
71 | break; |
72 | } |
73 | } |
74 | |
75 | static inline int error_check(struct error *err, const char *test_name) |
76 | { |
77 | /* In case of error we bail out and terminate the test program */ |
78 | if (err->code == PIDFD_ERROR) |
79 | error_report(err, test_name); |
80 | |
81 | return err->code; |
82 | } |
83 | |
84 | #define CHILD_STACK_SIZE 8192 |
85 | |
86 | struct child { |
87 | char *stack; |
88 | pid_t pid; |
89 | int fd; |
90 | }; |
91 | |
92 | static struct child clone_newns(int (*fn)(void *), void *args, |
93 | struct error *err) |
94 | { |
95 | static int flags = CLONE_PIDFD | CLONE_NEWPID | CLONE_NEWNS | SIGCHLD; |
96 | struct child ret; |
97 | |
98 | if (!(flags & CLONE_NEWUSER) && geteuid() != 0) |
99 | flags |= CLONE_NEWUSER; |
100 | |
101 | ret.stack = mmap(NULL, CHILD_STACK_SIZE, PROT_READ | PROT_WRITE, |
102 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0); |
103 | if (ret.stack == MAP_FAILED) { |
104 | error_set(err, code: -1, fmt: "mmap of stack failed (errno %d)" , errno); |
105 | return ret; |
106 | } |
107 | |
108 | #ifdef __ia64__ |
109 | ret.pid = __clone2(fn, ret.stack, CHILD_STACK_SIZE, flags, args, &ret.fd); |
110 | #else |
111 | ret.pid = clone(fn, ret.stack + CHILD_STACK_SIZE, flags, args, &ret.fd); |
112 | #endif |
113 | |
114 | if (ret.pid < 0) { |
115 | error_set(err, PIDFD_ERROR, fmt: "clone failed (ret %d, errno %d)" , |
116 | ret.fd, errno); |
117 | return ret; |
118 | } |
119 | |
120 | ksft_print_msg(msg: "New child: %d, fd: %d\n" , ret.pid, ret.fd); |
121 | |
122 | return ret; |
123 | } |
124 | |
125 | static inline void child_close(struct child *child) |
126 | { |
127 | close(child->fd); |
128 | } |
129 | |
130 | static inline int child_join(struct child *child, struct error *err) |
131 | { |
132 | int r; |
133 | |
134 | r = wait_for_pid(pid: child->pid); |
135 | if (r < 0) |
136 | error_set(err, PIDFD_ERROR, fmt: "waitpid failed (ret %d, errno %d)" , |
137 | r, errno); |
138 | else if (r > 0) |
139 | error_set(err, code: r, fmt: "child %d reported: %d" , child->pid, r); |
140 | |
141 | if (munmap(child->stack, CHILD_STACK_SIZE)) { |
142 | error_set(err, code: -1, fmt: "munmap of child stack failed (errno %d)" , errno); |
143 | r = -1; |
144 | } |
145 | |
146 | ksft_print_msg(msg: "waitpid WEXITSTATUS=%d\n" , r); |
147 | return r; |
148 | } |
149 | |
150 | static inline int child_join_close(struct child *child, struct error *err) |
151 | { |
152 | child_close(child); |
153 | return child_join(child, err); |
154 | } |
155 | |
156 | static inline void trim_newline(char *str) |
157 | { |
158 | char *pos = strrchr(str, '\n'); |
159 | |
160 | if (pos) |
161 | *pos = '\0'; |
162 | } |
163 | |
164 | static int verify_fdinfo(int pidfd, struct error *err, const char *prefix, |
165 | size_t prefix_len, const char *expect, ...) |
166 | { |
167 | char buffer[512] = {0, }; |
168 | char path[512] = {0, }; |
169 | va_list args; |
170 | FILE *f; |
171 | char *line = NULL; |
172 | size_t n = 0; |
173 | int found = 0; |
174 | int r; |
175 | |
176 | va_start(args, expect); |
177 | r = vsnprintf(buffer, sizeof(buffer), expect, args); |
178 | assert((size_t)r < sizeof(buffer)); |
179 | va_end(args); |
180 | |
181 | snprintf(path, sizeof(path), "/proc/self/fdinfo/%d" , pidfd); |
182 | f = fopen(path, "re" ); |
183 | if (!f) |
184 | return error_set(err, PIDFD_ERROR, fmt: "fdinfo open failed for %d" , |
185 | pidfd); |
186 | |
187 | while (getline(&line, &n, f) != -1) { |
188 | char *val; |
189 | |
190 | if (strncmp(line, prefix, prefix_len)) |
191 | continue; |
192 | |
193 | found = 1; |
194 | |
195 | val = line + prefix_len; |
196 | r = strcmp(val, buffer); |
197 | if (r != 0) { |
198 | trim_newline(str: line); |
199 | trim_newline(str: buffer); |
200 | error_set(err, PIDFD_FAIL, fmt: "%s '%s' != '%s'" , |
201 | prefix, val, buffer); |
202 | } |
203 | break; |
204 | } |
205 | |
206 | free(line); |
207 | fclose(f); |
208 | |
209 | if (found == 0) |
210 | return error_set(err, PIDFD_FAIL, fmt: "%s not found for fd %d" , |
211 | prefix, pidfd); |
212 | |
213 | return PIDFD_PASS; |
214 | } |
215 | |
216 | static int child_fdinfo_nspid_test(void *args) |
217 | { |
218 | struct error err; |
219 | int pidfd; |
220 | int r; |
221 | |
222 | /* if we got no fd for the sibling, we are done */ |
223 | if (!args) |
224 | return PIDFD_PASS; |
225 | |
226 | /* verify that we can not resolve the pidfd for a process |
227 | * in a sibling pid namespace, i.e. a pid namespace it is |
228 | * not in our or a descended namespace |
229 | */ |
230 | r = mount(NULL, "/" , NULL, MS_REC | MS_PRIVATE, 0); |
231 | if (r < 0) { |
232 | ksft_print_msg(msg: "Failed to remount / private\n" ); |
233 | return PIDFD_ERROR; |
234 | } |
235 | |
236 | (void)umount2("/proc" , MNT_DETACH); |
237 | r = mount("proc" , "/proc" , "proc" , 0, NULL); |
238 | if (r < 0) { |
239 | ksft_print_msg(msg: "Failed to remount /proc\n" ); |
240 | return PIDFD_ERROR; |
241 | } |
242 | |
243 | pidfd = *(int *)args; |
244 | r = verify_fdinfo(pidfd, err: &err, prefix: "NSpid:" , prefix_len: 6, expect: "\t0\n" ); |
245 | |
246 | if (r != PIDFD_PASS) |
247 | ksft_print_msg(msg: "NSpid fdinfo check failed: %s\n" , err.msg); |
248 | |
249 | return r; |
250 | } |
251 | |
252 | static void test_pidfd_fdinfo_nspid(void) |
253 | { |
254 | struct child a, b; |
255 | struct error err = {0, }; |
256 | const char *test_name = "pidfd check for NSpid in fdinfo" ; |
257 | |
258 | /* Create a new child in a new pid and mount namespace */ |
259 | a = clone_newns(fn: child_fdinfo_nspid_test, NULL, err: &err); |
260 | error_check(err: &err, test_name); |
261 | |
262 | /* Pass the pidfd representing the first child to the |
263 | * second child, which will be in a sibling pid namespace, |
264 | * which means that the fdinfo NSpid entry for the pidfd |
265 | * should only contain '0'. |
266 | */ |
267 | b = clone_newns(fn: child_fdinfo_nspid_test, args: &a.fd, err: &err); |
268 | error_check(err: &err, test_name); |
269 | |
270 | /* The children will have pid 1 in the new pid namespace, |
271 | * so the line must be 'NSPid:\t<pid>\t1'. |
272 | */ |
273 | verify_fdinfo(pidfd: a.fd, err: &err, prefix: "NSpid:" , prefix_len: 6, expect: "\t%d\t%d\n" , a.pid, 1); |
274 | verify_fdinfo(pidfd: b.fd, err: &err, prefix: "NSpid:" , prefix_len: 6, expect: "\t%d\t%d\n" , b.pid, 1); |
275 | |
276 | /* wait for the process, check the exit status and set |
277 | * 'err' accordingly, if it is not already set. |
278 | */ |
279 | child_join_close(child: &a, err: &err); |
280 | child_join_close(child: &b, err: &err); |
281 | |
282 | error_report(err: &err, test_name); |
283 | } |
284 | |
285 | static void test_pidfd_dead_fdinfo(void) |
286 | { |
287 | struct child a; |
288 | struct error err = {0, }; |
289 | const char *test_name = "pidfd check fdinfo for dead process" ; |
290 | |
291 | /* Create a new child in a new pid and mount namespace */ |
292 | a = clone_newns(fn: child_fdinfo_nspid_test, NULL, err: &err); |
293 | error_check(err: &err, test_name); |
294 | child_join(child: &a, err: &err); |
295 | |
296 | verify_fdinfo(pidfd: a.fd, err: &err, prefix: "Pid:" , prefix_len: 4, expect: "\t-1\n" ); |
297 | verify_fdinfo(pidfd: a.fd, err: &err, prefix: "NSpid:" , prefix_len: 6, expect: "\t-1\n" ); |
298 | child_close(child: &a); |
299 | error_report(err: &err, test_name); |
300 | } |
301 | |
302 | int main(int argc, char **argv) |
303 | { |
304 | ksft_print_header(); |
305 | ksft_set_plan(plan: 2); |
306 | |
307 | test_pidfd_fdinfo_nspid(); |
308 | test_pidfd_dead_fdinfo(); |
309 | |
310 | return ksft_exit_pass(); |
311 | } |
312 | |