linux/tools/testing/selftests/bpf/prog_tests/test_local_storage.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/*
   4 * Copyright (C) 2020 Google LLC.
   5 */
   6
   7#include <asm-generic/errno-base.h>
   8#include <sys/stat.h>
   9#include <test_progs.h>
  10#include <linux/limits.h>
  11
  12#include "local_storage.skel.h"
  13#include "network_helpers.h"
  14
  15#ifndef __NR_pidfd_open
  16#define __NR_pidfd_open 434
  17#endif
  18
  19static inline int sys_pidfd_open(pid_t pid, unsigned int flags)
  20{
  21        return syscall(__NR_pidfd_open, pid, flags);
  22}
  23
  24static unsigned int duration;
  25
  26#define TEST_STORAGE_VALUE 0xbeefdead
  27
  28struct storage {
  29        void *inode;
  30        unsigned int value;
  31        /* Lock ensures that spin locked versions of local stoage operations
  32         * also work, most operations in this tests are still single threaded
  33         */
  34        struct bpf_spin_lock lock;
  35};
  36
  37/* Fork and exec the provided rm binary and return the exit code of the
  38 * forked process and its pid.
  39 */
  40static int run_self_unlink(int *monitored_pid, const char *rm_path)
  41{
  42        int child_pid, child_status, ret;
  43        int null_fd;
  44
  45        child_pid = fork();
  46        if (child_pid == 0) {
  47                null_fd = open("/dev/null", O_WRONLY);
  48                dup2(null_fd, STDOUT_FILENO);
  49                dup2(null_fd, STDERR_FILENO);
  50                close(null_fd);
  51
  52                *monitored_pid = getpid();
  53                /* Use the copied /usr/bin/rm to delete itself
  54                 * /tmp/copy_of_rm /tmp/copy_of_rm.
  55                 */
  56                ret = execlp(rm_path, rm_path, rm_path, NULL);
  57                if (ret)
  58                        exit(errno);
  59        } else if (child_pid > 0) {
  60                waitpid(child_pid, &child_status, 0);
  61                return WEXITSTATUS(child_status);
  62        }
  63
  64        return -EINVAL;
  65}
  66
  67static bool check_syscall_operations(int map_fd, int obj_fd)
  68{
  69        struct storage val = { .value = TEST_STORAGE_VALUE, .lock = { 0 } },
  70                       lookup_val = { .value = 0, .lock = { 0 } };
  71        int err;
  72
  73        /* Looking up an existing element should fail initially */
  74        err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
  75                                        BPF_F_LOCK);
  76        if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
  77                  "err:%d errno:%d\n", err, errno))
  78                return false;
  79
  80        /* Create a new element */
  81        err = bpf_map_update_elem(map_fd, &obj_fd, &val,
  82                                  BPF_NOEXIST | BPF_F_LOCK);
  83        if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err,
  84                  errno))
  85                return false;
  86
  87        /* Lookup the newly created element */
  88        err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
  89                                        BPF_F_LOCK);
  90        if (CHECK(err < 0, "bpf_map_lookup_elem", "err:%d errno:%d", err,
  91                  errno))
  92                return false;
  93
  94        /* Check the value of the newly created element */
  95        if (CHECK(lookup_val.value != val.value, "bpf_map_lookup_elem",
  96                  "value got = %x errno:%d", lookup_val.value, val.value))
  97                return false;
  98
  99        err = bpf_map_delete_elem(map_fd, &obj_fd);
 100        if (CHECK(err, "bpf_map_delete_elem()", "err:%d errno:%d\n", err,
 101                  errno))
 102                return false;
 103
 104        /* The lookup should fail, now that the element has been deleted */
 105        err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val,
 106                                        BPF_F_LOCK);
 107        if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem",
 108                  "err:%d errno:%d\n", err, errno))
 109                return false;
 110
 111        return true;
 112}
 113
 114void test_test_local_storage(void)
 115{
 116        char tmp_dir_path[] = "/tmp/local_storageXXXXXX";
 117        int err, serv_sk = -1, task_fd = -1, rm_fd = -1;
 118        struct local_storage *skel = NULL;
 119        char tmp_exec_path[64];
 120        char cmd[256];
 121
 122        skel = local_storage__open_and_load();
 123        if (CHECK(!skel, "skel_load", "lsm skeleton failed\n"))
 124                goto close_prog;
 125
 126        err = local_storage__attach(skel);
 127        if (CHECK(err, "attach", "lsm attach failed: %d\n", err))
 128                goto close_prog;
 129
 130        task_fd = sys_pidfd_open(getpid(), 0);
 131        if (CHECK(task_fd < 0, "pidfd_open",
 132                  "failed to get pidfd err:%d, errno:%d", task_fd, errno))
 133                goto close_prog;
 134
 135        if (!check_syscall_operations(bpf_map__fd(skel->maps.task_storage_map),
 136                                      task_fd))
 137                goto close_prog;
 138
 139        if (CHECK(!mkdtemp(tmp_dir_path), "mkdtemp",
 140                  "unable to create tmpdir: %d\n", errno))
 141                goto close_prog;
 142
 143        snprintf(tmp_exec_path, sizeof(tmp_exec_path), "%s/copy_of_rm",
 144                 tmp_dir_path);
 145        snprintf(cmd, sizeof(cmd), "cp /bin/rm %s", tmp_exec_path);
 146        if (CHECK_FAIL(system(cmd)))
 147                goto close_prog_rmdir;
 148
 149        rm_fd = open(tmp_exec_path, O_RDONLY);
 150        if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d",
 151                  tmp_exec_path, rm_fd, errno))
 152                goto close_prog_rmdir;
 153
 154        if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map),
 155                                      rm_fd))
 156                goto close_prog_rmdir;
 157
 158        /* Sets skel->bss->monitored_pid to the pid of the forked child
 159         * forks a child process that executes tmp_exec_path and tries to
 160         * unlink its executable. This operation should be denied by the loaded
 161         * LSM program.
 162         */
 163        err = run_self_unlink(&skel->bss->monitored_pid, tmp_exec_path);
 164        if (CHECK(err != EPERM, "run_self_unlink", "err %d want EPERM\n", err))
 165                goto close_prog_rmdir;
 166
 167        /* Set the process being monitored to be the current process */
 168        skel->bss->monitored_pid = getpid();
 169
 170        /* Move copy_of_rm to a new location so that it triggers the
 171         * inode_rename LSM hook with a new_dentry that has a NULL inode ptr.
 172         */
 173        snprintf(cmd, sizeof(cmd), "mv %s/copy_of_rm %s/check_null_ptr",
 174                 tmp_dir_path, tmp_dir_path);
 175        if (CHECK_FAIL(system(cmd)))
 176                goto close_prog_rmdir;
 177
 178        CHECK(skel->data->inode_storage_result != 0, "inode_storage_result",
 179              "inode_local_storage not set\n");
 180
 181        serv_sk = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0);
 182        if (CHECK(serv_sk < 0, "start_server", "failed to start server\n"))
 183                goto close_prog_rmdir;
 184
 185        CHECK(skel->data->sk_storage_result != 0, "sk_storage_result",
 186              "sk_local_storage not set\n");
 187
 188        if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),
 189                                      serv_sk))
 190                goto close_prog_rmdir;
 191
 192close_prog_rmdir:
 193        snprintf(cmd, sizeof(cmd), "rm -rf %s", tmp_dir_path);
 194        system(cmd);
 195close_prog:
 196        close(serv_sk);
 197        close(rm_fd);
 198        close(task_fd);
 199        local_storage__destroy(skel);
 200}
 201