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