linux/tools/testing/selftests/clone3/clone3_cap_checkpoint_restore.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/*
   4 * Based on Christian Brauner's clone3() example.
   5 * These tests are assuming to be running in the host's
   6 * PID namespace.
   7 */
   8
   9/* capabilities related code based on selftests/bpf/test_verifier.c */
  10
  11#define _GNU_SOURCE
  12#include <errno.h>
  13#include <linux/types.h>
  14#include <linux/sched.h>
  15#include <stdio.h>
  16#include <stdlib.h>
  17#include <stdbool.h>
  18#include <sys/capability.h>
  19#include <sys/prctl.h>
  20#include <sys/syscall.h>
  21#include <sys/types.h>
  22#include <sys/un.h>
  23#include <sys/wait.h>
  24#include <unistd.h>
  25#include <sched.h>
  26
  27#include "../kselftest_harness.h"
  28#include "clone3_selftests.h"
  29
  30#ifndef MAX_PID_NS_LEVEL
  31#define MAX_PID_NS_LEVEL 32
  32#endif
  33
  34static void child_exit(int ret)
  35{
  36        fflush(stdout);
  37        fflush(stderr);
  38        _exit(ret);
  39}
  40
  41static int call_clone3_set_tid(struct __test_metadata *_metadata,
  42                               pid_t *set_tid, size_t set_tid_size)
  43{
  44        int status;
  45        pid_t pid = -1;
  46
  47        struct __clone_args args = {
  48                .exit_signal = SIGCHLD,
  49                .set_tid = ptr_to_u64(set_tid),
  50                .set_tid_size = set_tid_size,
  51        };
  52
  53        pid = sys_clone3(&args, sizeof(args));
  54        if (pid < 0) {
  55                TH_LOG("%s - Failed to create new process", strerror(errno));
  56                return -errno;
  57        }
  58
  59        if (pid == 0) {
  60                int ret;
  61                char tmp = 0;
  62
  63                TH_LOG("I am the child, my PID is %d (expected %d)", getpid(), set_tid[0]);
  64
  65                if (set_tid[0] != getpid())
  66                        child_exit(EXIT_FAILURE);
  67                child_exit(EXIT_SUCCESS);
  68        }
  69
  70        TH_LOG("I am the parent (%d). My child's pid is %d", getpid(), pid);
  71
  72        if (waitpid(pid, &status, 0) < 0) {
  73                TH_LOG("Child returned %s", strerror(errno));
  74                return -errno;
  75        }
  76
  77        if (!WIFEXITED(status))
  78                return -1;
  79
  80        return WEXITSTATUS(status);
  81}
  82
  83static int test_clone3_set_tid(struct __test_metadata *_metadata,
  84                               pid_t *set_tid, size_t set_tid_size)
  85{
  86        int ret;
  87
  88        TH_LOG("[%d] Trying clone3() with CLONE_SET_TID to %d", getpid(), set_tid[0]);
  89        ret = call_clone3_set_tid(_metadata, set_tid, set_tid_size);
  90        TH_LOG("[%d] clone3() with CLONE_SET_TID %d says:%d", getpid(), set_tid[0], ret);
  91        return ret;
  92}
  93
  94struct libcap {
  95        struct __user_cap_header_struct hdr;
  96        struct __user_cap_data_struct data[2];
  97};
  98
  99static int set_capability(void)
 100{
 101        cap_value_t cap_values[] = { CAP_SETUID, CAP_SETGID };
 102        struct libcap *cap;
 103        int ret = -1;
 104        cap_t caps;
 105
 106        caps = cap_get_proc();
 107        if (!caps) {
 108                perror("cap_get_proc");
 109                return -1;
 110        }
 111
 112        /* Drop all capabilities */
 113        if (cap_clear(caps)) {
 114                perror("cap_clear");
 115                goto out;
 116        }
 117
 118        cap_set_flag(caps, CAP_EFFECTIVE, 2, cap_values, CAP_SET);
 119        cap_set_flag(caps, CAP_PERMITTED, 2, cap_values, CAP_SET);
 120
 121        cap = (struct libcap *) caps;
 122
 123        /* 40 -> CAP_CHECKPOINT_RESTORE */
 124        cap->data[1].effective |= 1 << (40 - 32);
 125        cap->data[1].permitted |= 1 << (40 - 32);
 126
 127        if (cap_set_proc(caps)) {
 128                perror("cap_set_proc");
 129                goto out;
 130        }
 131        ret = 0;
 132out:
 133        if (cap_free(caps))
 134                perror("cap_free");
 135        return ret;
 136}
 137
 138TEST(clone3_cap_checkpoint_restore)
 139{
 140        pid_t pid;
 141        int status;
 142        int ret = 0;
 143        pid_t set_tid[1];
 144
 145        test_clone3_supported();
 146
 147        EXPECT_EQ(getuid(), 0)
 148                SKIP(return, "Skipping all tests as non-root");
 149
 150        memset(&set_tid, 0, sizeof(set_tid));
 151
 152        /* Find the current active PID */
 153        pid = fork();
 154        if (pid == 0) {
 155                TH_LOG("Child has PID %d", getpid());
 156                child_exit(EXIT_SUCCESS);
 157        }
 158        ASSERT_GT(waitpid(pid, &status, 0), 0)
 159                TH_LOG("Waiting for child %d failed", pid);
 160
 161        /* After the child has finished, its PID should be free. */
 162        set_tid[0] = pid;
 163
 164        ASSERT_EQ(set_capability(), 0)
 165                TH_LOG("Could not set CAP_CHECKPOINT_RESTORE");
 166
 167        ASSERT_EQ(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), 0);
 168
 169        EXPECT_EQ(setgid(65534), 0)
 170                TH_LOG("Failed to setgid(65534)");
 171        ASSERT_EQ(setuid(65534), 0);
 172
 173        set_tid[0] = pid;
 174        /* This would fail without CAP_CHECKPOINT_RESTORE */
 175        ASSERT_EQ(test_clone3_set_tid(_metadata, set_tid, 1), -EPERM);
 176        ASSERT_EQ(set_capability(), 0)
 177                TH_LOG("Could not set CAP_CHECKPOINT_RESTORE");
 178        /* This should work as we have CAP_CHECKPOINT_RESTORE as non-root */
 179        ASSERT_EQ(test_clone3_set_tid(_metadata, set_tid, 1), 0);
 180}
 181
 182TEST_HARNESS_MAIN
 183