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