Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pidfd: add tests for NSpid info in fdinfo

Add a test that checks that if pid namespaces are configured the fdinfo
file of a pidfd contains an NSpid: entry containing the process id in
the current and additionally all nested namespaces. In the case that
a pidfd is from a pid namespace not in the same namespace hierarchy as
the process accessing the fdinfo file, ensure the 'NSpid' shows 0 for
that pidfd, analogous to the 'Pid' entry.

Signed-off-by: Christian Kellner <christian@kellner.me>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
Link: https://lore.kernel.org/r/20191014162034.2185-2-ckellner@redhat.com
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>

authored by

Christian Kellner and committed by
Christian Brauner
2def297e 15d42eb2

+266 -1
+1 -1
tools/testing/selftests/pidfd/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 CFLAGS += -g -I../../../../usr/include/ -pthread 3 3 4 - TEST_GEN_PROGS := pidfd_test pidfd_open_test pidfd_poll_test pidfd_wait 4 + TEST_GEN_PROGS := pidfd_test pidfd_fdinfo_test pidfd_open_test pidfd_poll_test pidfd_wait 5 5 6 6 include ../lib.mk 7 7
+265
tools/testing/selftests/pidfd/pidfd_fdinfo_test.c
··· 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 int child_join(struct child *child, struct error *err) 117 + { 118 + int r; 119 + 120 + (void)close(child->fd); 121 + r = wait_for_pid(child->pid); 122 + if (r < 0) 123 + error_set(err, PIDFD_ERROR, "waitpid failed (ret %d, errno %d)", 124 + r, errno); 125 + else if (r > 0) 126 + error_set(err, r, "child %d reported: %d", child->pid, r); 127 + 128 + return r; 129 + } 130 + 131 + static inline void trim_newline(char *str) 132 + { 133 + char *pos = strrchr(str, '\n'); 134 + 135 + if (pos) 136 + *pos = '\0'; 137 + } 138 + 139 + static int verify_fdinfo_nspid(int pidfd, struct error *err, 140 + const char *expect, ...) 141 + { 142 + char buffer[512] = {0, }; 143 + char path[512] = {0, }; 144 + va_list args; 145 + FILE *f; 146 + char *line = NULL; 147 + size_t n = 0; 148 + int found = 0; 149 + int r; 150 + 151 + va_start(args, expect); 152 + r = vsnprintf(buffer, sizeof(buffer), expect, args); 153 + assert((size_t)r < sizeof(buffer)); 154 + va_end(args); 155 + 156 + snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", pidfd); 157 + f = fopen(path, "re"); 158 + if (!f) 159 + return error_set(err, PIDFD_ERROR, "fdinfo open failed for %d", 160 + pidfd); 161 + 162 + while (getline(&line, &n, f) != -1) { 163 + if (strncmp(line, "NSpid:", 6)) 164 + continue; 165 + 166 + found = 1; 167 + 168 + r = strcmp(line + 6, buffer); 169 + if (r != 0) { 170 + trim_newline(line); 171 + trim_newline(buffer); 172 + error_set(err, PIDFD_FAIL, "NSpid: '%s' != '%s'", 173 + line + 6, buffer); 174 + } 175 + break; 176 + } 177 + 178 + free(line); 179 + fclose(f); 180 + 181 + if (found == 0) 182 + return error_set(err, PIDFD_FAIL, "NSpid not found for fd %d", 183 + pidfd); 184 + 185 + return PIDFD_PASS; 186 + } 187 + 188 + static int child_fdinfo_nspid_test(void *args) 189 + { 190 + struct error err; 191 + int pidfd; 192 + int r; 193 + 194 + /* if we got no fd for the sibling, we are done */ 195 + if (!args) 196 + return PIDFD_PASS; 197 + 198 + /* verify that we can not resolve the pidfd for a process 199 + * in a sibling pid namespace, i.e. a pid namespace it is 200 + * not in our or a descended namespace 201 + */ 202 + r = mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0); 203 + if (r < 0) { 204 + ksft_print_msg("Failed to remount / private\n"); 205 + return PIDFD_ERROR; 206 + } 207 + 208 + (void)umount2("/proc", MNT_DETACH); 209 + r = mount("proc", "/proc", "proc", 0, NULL); 210 + if (r < 0) { 211 + ksft_print_msg("Failed to remount /proc\n"); 212 + return PIDFD_ERROR; 213 + } 214 + 215 + pidfd = *(int *)args; 216 + r = verify_fdinfo_nspid(pidfd, &err, "\t0\n"); 217 + 218 + if (r != PIDFD_PASS) 219 + ksft_print_msg("NSpid fdinfo check failed: %s\n", err.msg); 220 + 221 + return r; 222 + } 223 + 224 + static void test_pidfd_fdinfo_nspid(void) 225 + { 226 + struct child a, b; 227 + struct error err = {0, }; 228 + const char *test_name = "pidfd check for NSpid in fdinfo"; 229 + 230 + /* Create a new child in a new pid and mount namespace */ 231 + a = clone_newns(child_fdinfo_nspid_test, NULL, &err); 232 + error_check(&err, test_name); 233 + 234 + /* Pass the pidfd representing the first child to the 235 + * second child, which will be in a sibling pid namespace, 236 + * which means that the fdinfo NSpid entry for the pidfd 237 + * should only contain '0'. 238 + */ 239 + b = clone_newns(child_fdinfo_nspid_test, &a.fd, &err); 240 + error_check(&err, test_name); 241 + 242 + /* The children will have pid 1 in the new pid namespace, 243 + * so the line must be 'NSPid:\t<pid>\t1'. 244 + */ 245 + verify_fdinfo_nspid(a.fd, &err, "\t%d\t%d\n", a.pid, 1); 246 + verify_fdinfo_nspid(b.fd, &err, "\t%d\t%d\n", b.pid, 1); 247 + 248 + /* wait for the process, check the exit status and set 249 + * 'err' accordingly, if it is not already set. 250 + */ 251 + child_join(&a, &err); 252 + child_join(&b, &err); 253 + 254 + error_report(&err, test_name); 255 + } 256 + 257 + int main(int argc, char **argv) 258 + { 259 + ksft_print_header(); 260 + ksft_set_plan(1); 261 + 262 + test_pidfd_fdinfo_nspid(); 263 + 264 + return ksft_exit_pass(); 265 + }