linux/tools/testing/selftests/pidfd/pidfd_fdinfo_test.c
<<
>>
Prefs
   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
  19struct error {
  20        int  code;
  21        char msg[512];
  22};
  23
  24static 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
  41static 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
  74static 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
  83struct child {
  84        pid_t pid;
  85        int   fd;
  86};
  87
  88static 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
 116static inline void child_close(struct child *child)
 117{
 118        close(child->fd);
 119}
 120
 121static 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
 135static inline int child_join_close(struct child *child, struct error *err)
 136{
 137        child_close(child);
 138        return child_join(child, err);
 139}
 140
 141static inline void trim_newline(char *str)
 142{
 143        char *pos = strrchr(str, '\n');
 144
 145        if (pos)
 146                *pos = '\0';
 147}
 148
 149static 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
 201static 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
 237static 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
 270static 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
 287int 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