summaryrefslogtreecommitdiff
path: root/tools/testing/selftests/bpf/prog_tests/token.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests/token.c')
-rw-r--r--tools/testing/selftests/bpf/prog_tests/token.c1031
1 files changed, 0 insertions, 1031 deletions
diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c
deleted file mode 100644
index b5dce630e0e1..000000000000
--- a/tools/testing/selftests/bpf/prog_tests/token.c
+++ /dev/null
@@ -1,1031 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
-#define _GNU_SOURCE
-#include <test_progs.h>
-#include <bpf/btf.h>
-#include "cap_helpers.h"
-#include <fcntl.h>
-#include <sched.h>
-#include <signal.h>
-#include <unistd.h>
-#include <linux/filter.h>
-#include <linux/unistd.h>
-#include <linux/mount.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/syscall.h>
-#include <sys/un.h>
-#include "priv_map.skel.h"
-#include "priv_prog.skel.h"
-#include "dummy_st_ops_success.skel.h"
-
-static inline int sys_mount(const char *dev_name, const char *dir_name,
- const char *type, unsigned long flags,
- const void *data)
-{
- return syscall(__NR_mount, dev_name, dir_name, type, flags, data);
-}
-
-static inline int sys_fsopen(const char *fsname, unsigned flags)
-{
- return syscall(__NR_fsopen, fsname, flags);
-}
-
-static inline int sys_fspick(int dfd, const char *path, unsigned flags)
-{
- return syscall(__NR_fspick, dfd, path, flags);
-}
-
-static inline int sys_fsconfig(int fs_fd, unsigned cmd, const char *key, const void *val, int aux)
-{
- return syscall(__NR_fsconfig, fs_fd, cmd, key, val, aux);
-}
-
-static inline int sys_fsmount(int fs_fd, unsigned flags, unsigned ms_flags)
-{
- return syscall(__NR_fsmount, fs_fd, flags, ms_flags);
-}
-
-static inline int sys_move_mount(int from_dfd, const char *from_path,
- int to_dfd, const char *to_path,
- unsigned flags)
-{
- return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
-}
-
-static int drop_priv_caps(__u64 *old_caps)
-{
- return cap_disable_effective((1ULL << CAP_BPF) |
- (1ULL << CAP_PERFMON) |
- (1ULL << CAP_NET_ADMIN) |
- (1ULL << CAP_SYS_ADMIN), old_caps);
-}
-
-static int restore_priv_caps(__u64 old_caps)
-{
- return cap_enable_effective(old_caps, NULL);
-}
-
-static int set_delegate_mask(int fs_fd, const char *key, __u64 mask, const char *mask_str)
-{
- char buf[32];
- int err;
-
- if (!mask_str) {
- if (mask == ~0ULL) {
- mask_str = "any";
- } else {
- snprintf(buf, sizeof(buf), "0x%llx", (unsigned long long)mask);
- mask_str = buf;
- }
- }
-
- err = sys_fsconfig(fs_fd, FSCONFIG_SET_STRING, key,
- mask_str, 0);
- if (err < 0)
- err = -errno;
- return err;
-}
-
-#define zclose(fd) do { if (fd >= 0) close(fd); fd = -1; } while (0)
-
-struct bpffs_opts {
- __u64 cmds;
- __u64 maps;
- __u64 progs;
- __u64 attachs;
- const char *cmds_str;
- const char *maps_str;
- const char *progs_str;
- const char *attachs_str;
-};
-
-static int create_bpffs_fd(void)
-{
- int fs_fd;
-
- /* create VFS context */
- fs_fd = sys_fsopen("bpf", 0);
- ASSERT_GE(fs_fd, 0, "fs_fd");
-
- return fs_fd;
-}
-
-static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts)
-{
- int mnt_fd, err;
-
- /* set up token delegation mount options */
- err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds, opts->cmds_str);
- if (!ASSERT_OK(err, "fs_cfg_cmds"))
- return err;
- err = set_delegate_mask(fs_fd, "delegate_maps", opts->maps, opts->maps_str);
- if (!ASSERT_OK(err, "fs_cfg_maps"))
- return err;
- err = set_delegate_mask(fs_fd, "delegate_progs", opts->progs, opts->progs_str);
- if (!ASSERT_OK(err, "fs_cfg_progs"))
- return err;
- err = set_delegate_mask(fs_fd, "delegate_attachs", opts->attachs, opts->attachs_str);
- if (!ASSERT_OK(err, "fs_cfg_attachs"))
- return err;
-
- /* instantiate FS object */
- err = sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
- if (err < 0)
- return -errno;
-
- /* create O_PATH fd for detached mount */
- mnt_fd = sys_fsmount(fs_fd, 0, 0);
- if (err < 0)
- return -errno;
-
- return mnt_fd;
-}
-
-/* send FD over Unix domain (AF_UNIX) socket */
-static int sendfd(int sockfd, int fd)
-{
- struct msghdr msg = {};
- struct cmsghdr *cmsg;
- int fds[1] = { fd }, err;
- char iobuf[1];
- struct iovec io = {
- .iov_base = iobuf,
- .iov_len = sizeof(iobuf),
- };
- union {
- char buf[CMSG_SPACE(sizeof(fds))];
- struct cmsghdr align;
- } u;
-
- msg.msg_iov = &io;
- msg.msg_iovlen = 1;
- msg.msg_control = u.buf;
- msg.msg_controllen = sizeof(u.buf);
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
- memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
-
- err = sendmsg(sockfd, &msg, 0);
- if (err < 0)
- err = -errno;
- if (!ASSERT_EQ(err, 1, "sendmsg"))
- return -EINVAL;
-
- return 0;
-}
-
-/* receive FD over Unix domain (AF_UNIX) socket */
-static int recvfd(int sockfd, int *fd)
-{
- struct msghdr msg = {};
- struct cmsghdr *cmsg;
- int fds[1], err;
- char iobuf[1];
- struct iovec io = {
- .iov_base = iobuf,
- .iov_len = sizeof(iobuf),
- };
- union {
- char buf[CMSG_SPACE(sizeof(fds))];
- struct cmsghdr align;
- } u;
-
- msg.msg_iov = &io;
- msg.msg_iovlen = 1;
- msg.msg_control = u.buf;
- msg.msg_controllen = sizeof(u.buf);
-
- err = recvmsg(sockfd, &msg, 0);
- if (err < 0)
- err = -errno;
- if (!ASSERT_EQ(err, 1, "recvmsg"))
- return -EINVAL;
-
- cmsg = CMSG_FIRSTHDR(&msg);
- if (!ASSERT_OK_PTR(cmsg, "cmsg_null") ||
- !ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(fds)), "cmsg_len") ||
- !ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET, "cmsg_level") ||
- !ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS, "cmsg_type"))
- return -EINVAL;
-
- memcpy(fds, CMSG_DATA(cmsg), sizeof(fds));
- *fd = fds[0];
-
- return 0;
-}
-
-static ssize_t write_nointr(int fd, const void *buf, size_t count)
-{
- ssize_t ret;
-
- do {
- ret = write(fd, buf, count);
- } while (ret < 0 && errno == EINTR);
-
- return ret;
-}
-
-static int write_file(const char *path, const void *buf, size_t count)
-{
- int fd;
- ssize_t ret;
-
- fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
- if (fd < 0)
- return -1;
-
- ret = write_nointr(fd, buf, count);
- close(fd);
- if (ret < 0 || (size_t)ret != count)
- return -1;
-
- return 0;
-}
-
-static int create_and_enter_userns(void)
-{
- uid_t uid;
- gid_t gid;
- char map[100];
-
- uid = getuid();
- gid = getgid();
-
- if (unshare(CLONE_NEWUSER))
- return -1;
-
- if (write_file("/proc/self/setgroups", "deny", sizeof("deny") - 1) &&
- errno != ENOENT)
- return -1;
-
- snprintf(map, sizeof(map), "0 %d 1", uid);
- if (write_file("/proc/self/uid_map", map, strlen(map)))
- return -1;
-
-
- snprintf(map, sizeof(map), "0 %d 1", gid);
- if (write_file("/proc/self/gid_map", map, strlen(map)))
- return -1;
-
- if (setgid(0))
- return -1;
-
- if (setuid(0))
- return -1;
-
- return 0;
-}
-
-typedef int (*child_callback_fn)(int);
-
-static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callback)
-{
- LIBBPF_OPTS(bpf_map_create_opts, map_opts);
- int mnt_fd = -1, fs_fd = -1, err = 0, bpffs_fd = -1;
-
- /* setup userns with root mappings */
- err = create_and_enter_userns();
- if (!ASSERT_OK(err, "create_and_enter_userns"))
- goto cleanup;
-
- /* setup mountns to allow creating BPF FS (fsopen("bpf")) from unpriv process */
- err = unshare(CLONE_NEWNS);
- if (!ASSERT_OK(err, "create_mountns"))
- goto cleanup;
-
- err = sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0);
- if (!ASSERT_OK(err, "remount_root"))
- goto cleanup;
-
- fs_fd = create_bpffs_fd();
- if (!ASSERT_GE(fs_fd, 0, "create_bpffs_fd")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* ensure unprivileged child cannot set delegation options */
- err = set_delegate_mask(fs_fd, "delegate_cmds", 0x1, NULL);
- ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm");
- err = set_delegate_mask(fs_fd, "delegate_maps", 0x1, NULL);
- ASSERT_EQ(err, -EPERM, "delegate_maps_eperm");
- err = set_delegate_mask(fs_fd, "delegate_progs", 0x1, NULL);
- ASSERT_EQ(err, -EPERM, "delegate_progs_eperm");
- err = set_delegate_mask(fs_fd, "delegate_attachs", 0x1, NULL);
- ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm");
-
- /* pass BPF FS context object to parent */
- err = sendfd(sock_fd, fs_fd);
- if (!ASSERT_OK(err, "send_fs_fd"))
- goto cleanup;
- zclose(fs_fd);
-
- /* avoid mucking around with mount namespaces and mounting at
- * well-known path, just get detach-mounted BPF FS fd back from parent
- */
- err = recvfd(sock_fd, &mnt_fd);
- if (!ASSERT_OK(err, "recv_mnt_fd"))
- goto cleanup;
-
- /* try to fspick() BPF FS and try to add some delegation options */
- fs_fd = sys_fspick(mnt_fd, "", FSPICK_EMPTY_PATH);
- if (!ASSERT_GE(fs_fd, 0, "bpffs_fspick")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* ensure unprivileged child cannot reconfigure to set delegation options */
- err = set_delegate_mask(fs_fd, "delegate_cmds", 0, "any");
- if (!ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm_reconfig")) {
- err = -EINVAL;
- goto cleanup;
- }
- err = set_delegate_mask(fs_fd, "delegate_maps", 0, "any");
- if (!ASSERT_EQ(err, -EPERM, "delegate_maps_eperm_reconfig")) {
- err = -EINVAL;
- goto cleanup;
- }
- err = set_delegate_mask(fs_fd, "delegate_progs", 0, "any");
- if (!ASSERT_EQ(err, -EPERM, "delegate_progs_eperm_reconfig")) {
- err = -EINVAL;
- goto cleanup;
- }
- err = set_delegate_mask(fs_fd, "delegate_attachs", 0, "any");
- if (!ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm_reconfig")) {
- err = -EINVAL;
- goto cleanup;
- }
- zclose(fs_fd);
-
- bpffs_fd = openat(mnt_fd, ".", 0, O_RDWR);
- if (!ASSERT_GE(bpffs_fd, 0, "bpffs_open")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* do custom test logic with customly set up BPF FS instance */
- err = callback(bpffs_fd);
- if (!ASSERT_OK(err, "test_callback"))
- goto cleanup;
-
- err = 0;
-cleanup:
- zclose(sock_fd);
- zclose(mnt_fd);
- zclose(fs_fd);
- zclose(bpffs_fd);
-
- exit(-err);
-}
-
-static int wait_for_pid(pid_t pid)
-{
- int status, ret;
-
-again:
- ret = waitpid(pid, &status, 0);
- if (ret == -1) {
- if (errno == EINTR)
- goto again;
-
- return -1;
- }
-
- if (!WIFEXITED(status))
- return -1;
-
- return WEXITSTATUS(status);
-}
-
-static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd)
-{
- int fs_fd = -1, mnt_fd = -1, err;
-
- err = recvfd(sock_fd, &fs_fd);
- if (!ASSERT_OK(err, "recv_bpffs_fd"))
- goto cleanup;
-
- mnt_fd = materialize_bpffs_fd(fs_fd, bpffs_opts);
- if (!ASSERT_GE(mnt_fd, 0, "materialize_bpffs_fd")) {
- err = -EINVAL;
- goto cleanup;
- }
- zclose(fs_fd);
-
- /* pass BPF FS context object to parent */
- err = sendfd(sock_fd, mnt_fd);
- if (!ASSERT_OK(err, "send_mnt_fd"))
- goto cleanup;
- zclose(mnt_fd);
-
- err = wait_for_pid(child_pid);
- ASSERT_OK(err, "waitpid_child");
-
-cleanup:
- zclose(sock_fd);
- zclose(fs_fd);
- zclose(mnt_fd);
-
- if (child_pid > 0)
- (void)kill(child_pid, SIGKILL);
-}
-
-static void subtest_userns(struct bpffs_opts *bpffs_opts, child_callback_fn cb)
-{
- int sock_fds[2] = { -1, -1 };
- int child_pid = 0, err;
-
- err = socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds);
- if (!ASSERT_OK(err, "socketpair"))
- goto cleanup;
-
- child_pid = fork();
- if (!ASSERT_GE(child_pid, 0, "fork"))
- goto cleanup;
-
- if (child_pid == 0) {
- zclose(sock_fds[0]);
- return child(sock_fds[1], bpffs_opts, cb);
-
- } else {
- zclose(sock_fds[1]);
- return parent(child_pid, bpffs_opts, sock_fds[0]);
- }
-
-cleanup:
- zclose(sock_fds[0]);
- zclose(sock_fds[1]);
- if (child_pid > 0)
- (void)kill(child_pid, SIGKILL);
-}
-
-static int userns_map_create(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_map_create_opts, map_opts);
- int err, token_fd = -1, map_fd = -1;
- __u64 old_caps = 0;
-
- /* create BPF token from BPF FS mount */
- token_fd = bpf_token_create(mnt_fd, NULL);
- if (!ASSERT_GT(token_fd, 0, "token_create")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* while inside non-init userns, we need both a BPF token *and*
- * CAP_BPF inside current userns to create privileged map; let's test
- * that neither BPF token alone nor namespaced CAP_BPF is sufficient
- */
- err = drop_priv_caps(&old_caps);
- if (!ASSERT_OK(err, "drop_caps"))
- goto cleanup;
-
- /* no token, no CAP_BPF -> fail */
- map_opts.token_fd = 0;
- map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "wo_token_wo_bpf", 0, 8, 1, &map_opts);
- if (!ASSERT_LT(map_fd, 0, "stack_map_wo_token_wo_cap_bpf_should_fail")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* token without CAP_BPF -> fail */
- map_opts.token_fd = token_fd;
- map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "w_token_wo_bpf", 0, 8, 1, &map_opts);
- if (!ASSERT_LT(map_fd, 0, "stack_map_w_token_wo_cap_bpf_should_fail")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* get back effective local CAP_BPF (and CAP_SYS_ADMIN) */
- err = restore_priv_caps(old_caps);
- if (!ASSERT_OK(err, "restore_caps"))
- goto cleanup;
-
- /* CAP_BPF without token -> fail */
- map_opts.token_fd = 0;
- map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "wo_token_w_bpf", 0, 8, 1, &map_opts);
- if (!ASSERT_LT(map_fd, 0, "stack_map_wo_token_w_cap_bpf_should_fail")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* finally, namespaced CAP_BPF + token -> success */
- map_opts.token_fd = token_fd;
- map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "w_token_w_bpf", 0, 8, 1, &map_opts);
- if (!ASSERT_GT(map_fd, 0, "stack_map_w_token_w_cap_bpf")) {
- err = -EINVAL;
- goto cleanup;
- }
-
-cleanup:
- zclose(token_fd);
- zclose(map_fd);
- return err;
-}
-
-static int userns_btf_load(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_btf_load_opts, btf_opts);
- int err, token_fd = -1, btf_fd = -1;
- const void *raw_btf_data;
- struct btf *btf = NULL;
- __u32 raw_btf_size;
- __u64 old_caps = 0;
-
- /* create BPF token from BPF FS mount */
- token_fd = bpf_token_create(mnt_fd, NULL);
- if (!ASSERT_GT(token_fd, 0, "token_create")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* while inside non-init userns, we need both a BPF token *and*
- * CAP_BPF inside current userns to create privileged map; let's test
- * that neither BPF token alone nor namespaced CAP_BPF is sufficient
- */
- err = drop_priv_caps(&old_caps);
- if (!ASSERT_OK(err, "drop_caps"))
- goto cleanup;
-
- /* setup a trivial BTF data to load to the kernel */
- btf = btf__new_empty();
- if (!ASSERT_OK_PTR(btf, "empty_btf"))
- goto cleanup;
-
- ASSERT_GT(btf__add_int(btf, "int", 4, 0), 0, "int_type");
-
- raw_btf_data = btf__raw_data(btf, &raw_btf_size);
- if (!ASSERT_OK_PTR(raw_btf_data, "raw_btf_data"))
- goto cleanup;
-
- /* no token + no CAP_BPF -> failure */
- btf_opts.token_fd = 0;
- btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts);
- if (!ASSERT_LT(btf_fd, 0, "no_token_no_cap_should_fail"))
- goto cleanup;
-
- /* token + no CAP_BPF -> failure */
- btf_opts.token_fd = token_fd;
- btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts);
- if (!ASSERT_LT(btf_fd, 0, "token_no_cap_should_fail"))
- goto cleanup;
-
- /* get back effective local CAP_BPF (and CAP_SYS_ADMIN) */
- err = restore_priv_caps(old_caps);
- if (!ASSERT_OK(err, "restore_caps"))
- goto cleanup;
-
- /* token + CAP_BPF -> success */
- btf_opts.token_fd = token_fd;
- btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts);
- if (!ASSERT_GT(btf_fd, 0, "token_and_cap_success"))
- goto cleanup;
-
- err = 0;
-cleanup:
- btf__free(btf);
- zclose(btf_fd);
- zclose(token_fd);
- return err;
-}
-
-static int userns_prog_load(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_prog_load_opts, prog_opts);
- int err, token_fd = -1, prog_fd = -1;
- struct bpf_insn insns[] = {
- /* bpf_jiffies64() requires CAP_BPF */
- BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64),
- /* bpf_get_current_task() requires CAP_PERFMON */
- BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_current_task),
- /* r0 = 0; exit; */
- BPF_MOV64_IMM(BPF_REG_0, 0),
- BPF_EXIT_INSN(),
- };
- size_t insn_cnt = ARRAY_SIZE(insns);
- __u64 old_caps = 0;
-
- /* create BPF token from BPF FS mount */
- token_fd = bpf_token_create(mnt_fd, NULL);
- if (!ASSERT_GT(token_fd, 0, "token_create")) {
- err = -EINVAL;
- goto cleanup;
- }
-
- /* validate we can successfully load BPF program with token; this
- * being XDP program (CAP_NET_ADMIN) using bpf_jiffies64() (CAP_BPF)
- * and bpf_get_current_task() (CAP_PERFMON) helpers validates we have
- * BPF token wired properly in a bunch of places in the kernel
- */
- prog_opts.token_fd = token_fd;
- prog_opts.expected_attach_type = BPF_XDP;
- prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL",
- insns, insn_cnt, &prog_opts);
- if (!ASSERT_GT(prog_fd, 0, "prog_fd")) {
- err = -EPERM;
- goto cleanup;
- }
-
- /* no token + caps -> failure */
- prog_opts.token_fd = 0;
- prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL",
- insns, insn_cnt, &prog_opts);
- if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) {
- err = -EPERM;
- goto cleanup;
- }
-
- err = drop_priv_caps(&old_caps);
- if (!ASSERT_OK(err, "drop_caps"))
- goto cleanup;
-
- /* no caps + token -> failure */
- prog_opts.token_fd = token_fd;
- prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL",
- insns, insn_cnt, &prog_opts);
- if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) {
- err = -EPERM;
- goto cleanup;
- }
-
- /* no caps + no token -> definitely a failure */
- prog_opts.token_fd = 0;
- prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL",
- insns, insn_cnt, &prog_opts);
- if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) {
- err = -EPERM;
- goto cleanup;
- }
-
- err = 0;
-cleanup:
- zclose(prog_fd);
- zclose(token_fd);
- return err;
-}
-
-static int userns_obj_priv_map(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts);
- char buf[256];
- struct priv_map *skel;
- int err, token_fd;
-
- skel = priv_map__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) {
- priv_map__destroy(skel);
- return -EINVAL;
- }
-
- /* use bpf_token_path to provide BPF FS path */
- snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
- opts.bpf_token_path = buf;
- skel = priv_map__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_token_path_open"))
- return -EINVAL;
-
- err = priv_map__load(skel);
- priv_map__destroy(skel);
- if (!ASSERT_OK(err, "obj_token_path_load"))
- return -EINVAL;
-
- /* create token and pass it through bpf_token_fd */
- token_fd = bpf_token_create(mnt_fd, NULL);
- if (!ASSERT_GT(token_fd, 0, "create_token"))
- return -EINVAL;
-
- opts.bpf_token_path = NULL;
- opts.bpf_token_fd = token_fd;
- skel = priv_map__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_token_fd_open"))
- return -EINVAL;
-
- /* we can close our token FD, bpf_object owns dup()'ed FD now */
- close(token_fd);
-
- err = priv_map__load(skel);
- priv_map__destroy(skel);
- if (!ASSERT_OK(err, "obj_token_fd_load"))
- return -EINVAL;
-
- return 0;
-}
-
-static int userns_obj_priv_prog(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts);
- char buf[256];
- struct priv_prog *skel;
- int err;
-
- skel = priv_prog__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) {
- priv_prog__destroy(skel);
- return -EINVAL;
- }
-
- /* use bpf_token_path to provide BPF FS path */
- snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
- opts.bpf_token_path = buf;
- skel = priv_prog__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_token_path_open"))
- return -EINVAL;
-
- err = priv_prog__load(skel);
- priv_prog__destroy(skel);
- if (!ASSERT_OK(err, "obj_token_path_load"))
- return -EINVAL;
-
- return 0;
-}
-
-/* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command,
- * which should cause struct_ops application to fail, as BTF won't be uploaded
- * into the kernel, even if STRUCT_OPS programs themselves are allowed
- */
-static int validate_struct_ops_load(int mnt_fd, bool expect_success)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts);
- char buf[256];
- struct dummy_st_ops_success *skel;
- int err;
-
- snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
- opts.bpf_token_path = buf;
- skel = dummy_st_ops_success__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_token_path_open"))
- return -EINVAL;
-
- err = dummy_st_ops_success__load(skel);
- dummy_st_ops_success__destroy(skel);
- if (expect_success) {
- if (!ASSERT_OK(err, "obj_token_path_load"))
- return -EINVAL;
- } else /* expect failure */ {
- if (!ASSERT_ERR(err, "obj_token_path_load"))
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int userns_obj_priv_btf_fail(int mnt_fd)
-{
- return validate_struct_ops_load(mnt_fd, false /* should fail */);
-}
-
-static int userns_obj_priv_btf_success(int mnt_fd)
-{
- return validate_struct_ops_load(mnt_fd, true /* should succeed */);
-}
-
-#define TOKEN_ENVVAR "LIBBPF_BPF_TOKEN_PATH"
-#define TOKEN_BPFFS_CUSTOM "/bpf-token-fs"
-
-static int userns_obj_priv_implicit_token(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts);
- struct dummy_st_ops_success *skel;
- int err;
-
- /* before we mount BPF FS with token delegation, struct_ops skeleton
- * should fail to load
- */
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) {
- dummy_st_ops_success__destroy(skel);
- return -EINVAL;
- }
-
- /* mount custom BPF FS over /sys/fs/bpf so that libbpf can create BPF
- * token automatically and implicitly
- */
- err = sys_move_mount(mnt_fd, "", AT_FDCWD, "/sys/fs/bpf", MOVE_MOUNT_F_EMPTY_PATH);
- if (!ASSERT_OK(err, "move_mount_bpffs"))
- return -EINVAL;
-
- /* disable implicit BPF token creation by setting
- * LIBBPF_BPF_TOKEN_PATH envvar to empty value, load should fail
- */
- err = setenv(TOKEN_ENVVAR, "", 1 /*overwrite*/);
- if (!ASSERT_OK(err, "setenv_token_path"))
- return -EINVAL;
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_token_envvar_disabled_load")) {
- unsetenv(TOKEN_ENVVAR);
- dummy_st_ops_success__destroy(skel);
- return -EINVAL;
- }
- unsetenv(TOKEN_ENVVAR);
-
- /* now the same struct_ops skeleton should succeed thanks to libppf
- * creating BPF token from /sys/fs/bpf mount point
- */
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_OK_PTR(skel, "obj_implicit_token_load"))
- return -EINVAL;
-
- dummy_st_ops_success__destroy(skel);
-
- /* now disable implicit token through empty bpf_token_path, should fail */
- opts.bpf_token_path = "";
- skel = dummy_st_ops_success__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_empty_token_path_open"))
- return -EINVAL;
-
- err = dummy_st_ops_success__load(skel);
- dummy_st_ops_success__destroy(skel);
- if (!ASSERT_ERR(err, "obj_empty_token_path_load"))
- return -EINVAL;
-
- /* now disable implicit token through negative bpf_token_fd, should fail */
- opts.bpf_token_path = NULL;
- opts.bpf_token_fd = -1;
- skel = dummy_st_ops_success__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_neg_token_fd_open"))
- return -EINVAL;
-
- err = dummy_st_ops_success__load(skel);
- dummy_st_ops_success__destroy(skel);
- if (!ASSERT_ERR(err, "obj_neg_token_fd_load"))
- return -EINVAL;
-
- return 0;
-}
-
-static int userns_obj_priv_implicit_token_envvar(int mnt_fd)
-{
- LIBBPF_OPTS(bpf_object_open_opts, opts);
- struct dummy_st_ops_success *skel;
- int err;
-
- /* before we mount BPF FS with token delegation, struct_ops skeleton
- * should fail to load
- */
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) {
- dummy_st_ops_success__destroy(skel);
- return -EINVAL;
- }
-
- /* mount custom BPF FS over custom location, so libbpf can't create
- * BPF token implicitly, unless pointed to it through
- * LIBBPF_BPF_TOKEN_PATH envvar
- */
- rmdir(TOKEN_BPFFS_CUSTOM);
- if (!ASSERT_OK(mkdir(TOKEN_BPFFS_CUSTOM, 0777), "mkdir_bpffs_custom"))
- goto err_out;
- err = sys_move_mount(mnt_fd, "", AT_FDCWD, TOKEN_BPFFS_CUSTOM, MOVE_MOUNT_F_EMPTY_PATH);
- if (!ASSERT_OK(err, "move_mount_bpffs"))
- goto err_out;
-
- /* even though we have BPF FS with delegation, it's not at default
- * /sys/fs/bpf location, so we still fail to load until envvar is set up
- */
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load2")) {
- dummy_st_ops_success__destroy(skel);
- goto err_out;
- }
-
- err = setenv(TOKEN_ENVVAR, TOKEN_BPFFS_CUSTOM, 1 /*overwrite*/);
- if (!ASSERT_OK(err, "setenv_token_path"))
- goto err_out;
-
- /* now the same struct_ops skeleton should succeed thanks to libppf
- * creating BPF token from custom mount point
- */
- skel = dummy_st_ops_success__open_and_load();
- if (!ASSERT_OK_PTR(skel, "obj_implicit_token_load"))
- goto err_out;
-
- dummy_st_ops_success__destroy(skel);
-
- /* now disable implicit token through empty bpf_token_path, envvar
- * will be ignored, should fail
- */
- opts.bpf_token_path = "";
- skel = dummy_st_ops_success__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_empty_token_path_open"))
- goto err_out;
-
- err = dummy_st_ops_success__load(skel);
- dummy_st_ops_success__destroy(skel);
- if (!ASSERT_ERR(err, "obj_empty_token_path_load"))
- goto err_out;
-
- /* now disable implicit token through negative bpf_token_fd, envvar
- * will be ignored, should fail
- */
- opts.bpf_token_path = NULL;
- opts.bpf_token_fd = -1;
- skel = dummy_st_ops_success__open_opts(&opts);
- if (!ASSERT_OK_PTR(skel, "obj_neg_token_fd_open"))
- goto err_out;
-
- err = dummy_st_ops_success__load(skel);
- dummy_st_ops_success__destroy(skel);
- if (!ASSERT_ERR(err, "obj_neg_token_fd_load"))
- goto err_out;
-
- rmdir(TOKEN_BPFFS_CUSTOM);
- unsetenv(TOKEN_ENVVAR);
- return 0;
-err_out:
- rmdir(TOKEN_BPFFS_CUSTOM);
- unsetenv(TOKEN_ENVVAR);
- return -EINVAL;
-}
-
-#define bit(n) (1ULL << (n))
-
-void test_token(void)
-{
- if (test__start_subtest("map_token")) {
- struct bpffs_opts opts = {
- .cmds_str = "map_create",
- .maps_str = "stack",
- };
-
- subtest_userns(&opts, userns_map_create);
- }
- if (test__start_subtest("btf_token")) {
- struct bpffs_opts opts = {
- .cmds = 1ULL << BPF_BTF_LOAD,
- };
-
- subtest_userns(&opts, userns_btf_load);
- }
- if (test__start_subtest("prog_token")) {
- struct bpffs_opts opts = {
- .cmds_str = "PROG_LOAD",
- .progs_str = "XDP",
- .attachs_str = "xdp",
- };
-
- subtest_userns(&opts, userns_prog_load);
- }
- if (test__start_subtest("obj_priv_map")) {
- struct bpffs_opts opts = {
- .cmds = bit(BPF_MAP_CREATE),
- .maps = bit(BPF_MAP_TYPE_QUEUE),
- };
-
- subtest_userns(&opts, userns_obj_priv_map);
- }
- if (test__start_subtest("obj_priv_prog")) {
- struct bpffs_opts opts = {
- .cmds = bit(BPF_PROG_LOAD),
- .progs = bit(BPF_PROG_TYPE_KPROBE),
- .attachs = ~0ULL,
- };
-
- subtest_userns(&opts, userns_obj_priv_prog);
- }
- if (test__start_subtest("obj_priv_btf_fail")) {
- struct bpffs_opts opts = {
- /* disallow BTF loading */
- .cmds = bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD),
- .maps = bit(BPF_MAP_TYPE_STRUCT_OPS),
- .progs = bit(BPF_PROG_TYPE_STRUCT_OPS),
- .attachs = ~0ULL,
- };
-
- subtest_userns(&opts, userns_obj_priv_btf_fail);
- }
- if (test__start_subtest("obj_priv_btf_success")) {
- struct bpffs_opts opts = {
- /* allow BTF loading */
- .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD),
- .maps = bit(BPF_MAP_TYPE_STRUCT_OPS),
- .progs = bit(BPF_PROG_TYPE_STRUCT_OPS),
- .attachs = ~0ULL,
- };
-
- subtest_userns(&opts, userns_obj_priv_btf_success);
- }
- if (test__start_subtest("obj_priv_implicit_token")) {
- struct bpffs_opts opts = {
- /* allow BTF loading */
- .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD),
- .maps = bit(BPF_MAP_TYPE_STRUCT_OPS),
- .progs = bit(BPF_PROG_TYPE_STRUCT_OPS),
- .attachs = ~0ULL,
- };
-
- subtest_userns(&opts, userns_obj_priv_implicit_token);
- }
- if (test__start_subtest("obj_priv_implicit_token_envvar")) {
- struct bpffs_opts opts = {
- /* allow BTF loading */
- .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD),
- .maps = bit(BPF_MAP_TYPE_STRUCT_OPS),
- .progs = bit(BPF_PROG_TYPE_STRUCT_OPS),
- .attachs = ~0ULL,
- };
-
- subtest_userns(&opts, userns_obj_priv_implicit_token_envvar);
- }
-}