selftests/bpf: Test freplace from user namespace

Add selftests to verify that it is possible to load freplace program
from user namespace if BPF token is initialized by bpf_object__prepare
before calling bpf_program__set_attach_target.
Negative test is added as well.

Modified type of the priv_prog to xdp, as kprobe did not work on aarch64
and s390x.

Signed-off-by: Mykyta Yatsenko <yatsenko@meta.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/bpf/20250317174039.161275-5-mykyta.yatsenko5@gmail.com
This commit is contained in:
Mykyta Yatsenko
2025-03-17 17:40:39 +00:00
committed by Andrii Nakryiko
parent 974ef9f0d2
commit a024843d92
3 changed files with 112 additions and 4 deletions

View File

@@ -19,6 +19,7 @@
#include "priv_prog.skel.h"
#include "dummy_st_ops_success.skel.h"
#include "token_lsm.skel.h"
#include "priv_freplace_prog.skel.h"
static inline int sys_mount(const char *dev_name, const char *dir_name,
const char *type, unsigned long flags,
@@ -788,6 +789,84 @@ static int userns_obj_priv_prog(int mnt_fd, struct token_lsm *lsm_skel)
return 0;
}
static int userns_obj_priv_freplace_setup(int mnt_fd, struct priv_freplace_prog **fr_skel,
struct priv_prog **skel, int *tgt_fd)
{
LIBBPF_OPTS(bpf_object_open_opts, opts);
int err;
char buf[256];
/* 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, "priv_prog__open_opts"))
return -EINVAL;
err = priv_prog__load(*skel);
if (!ASSERT_OK(err, "priv_prog__load"))
return -EINVAL;
*fr_skel = priv_freplace_prog__open_opts(&opts);
if (!ASSERT_OK_PTR(*skel, "priv_freplace_prog__open_opts"))
return -EINVAL;
*tgt_fd = bpf_program__fd((*skel)->progs.xdp_prog1);
return 0;
}
/* Verify that freplace works from user namespace, because bpf token is loaded
* in bpf_object__prepare
*/
static int userns_obj_priv_freplace_prog(int mnt_fd, struct token_lsm *lsm_skel)
{
struct priv_freplace_prog *fr_skel = NULL;
struct priv_prog *skel = NULL;
int err, tgt_fd;
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
if (!ASSERT_OK(err, "setup"))
goto out;
err = bpf_object__prepare(fr_skel->obj);
if (!ASSERT_OK(err, "freplace__prepare"))
goto out;
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
if (!ASSERT_OK(err, "set_attach_target"))
goto out;
err = priv_freplace_prog__load(fr_skel);
ASSERT_OK(err, "priv_freplace_prog__load");
out:
priv_freplace_prog__destroy(fr_skel);
priv_prog__destroy(skel);
return err;
}
/* Verify that replace fails to set attach target from user namespace without bpf token */
static int userns_obj_priv_freplace_prog_fail(int mnt_fd, struct token_lsm *lsm_skel)
{
struct priv_freplace_prog *fr_skel = NULL;
struct priv_prog *skel = NULL;
int err, tgt_fd;
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
if (!ASSERT_OK(err, "setup"))
goto out;
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
if (ASSERT_ERR(err, "attach fails"))
err = 0;
else
err = -EINVAL;
out:
priv_freplace_prog__destroy(fr_skel);
priv_prog__destroy(skel);
return err;
}
/* 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
@@ -1004,12 +1083,28 @@ void test_token(void)
if (test__start_subtest("obj_priv_prog")) {
struct bpffs_opts opts = {
.cmds = bit(BPF_PROG_LOAD),
.progs = bit(BPF_PROG_TYPE_KPROBE),
.progs = bit(BPF_PROG_TYPE_XDP),
.attachs = ~0ULL,
};
subtest_userns(&opts, userns_obj_priv_prog);
}
if (test__start_subtest("obj_priv_freplace_prog")) {
struct bpffs_opts opts = {
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
.attachs = ~0ULL,
};
subtest_userns(&opts, userns_obj_priv_freplace_prog);
}
if (test__start_subtest("obj_priv_freplace_prog_fail")) {
struct bpffs_opts opts = {
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
.attachs = ~0ULL,
};
subtest_userns(&opts, userns_obj_priv_freplace_prog_fail);
}
if (test__start_subtest("obj_priv_btf_fail")) {
struct bpffs_opts opts = {
/* disallow BTF loading */