Skip to content

Commit a024843

Browse files
mykyta5anakryiko
authored andcommitted
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
1 parent 974ef9f commit a024843

File tree

3 files changed

+112
-4
lines changed

3 files changed

+112
-4
lines changed

tools/testing/selftests/bpf/prog_tests/token.c

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "priv_prog.skel.h"
2020
#include "dummy_st_ops_success.skel.h"
2121
#include "token_lsm.skel.h"
22+
#include "priv_freplace_prog.skel.h"
2223

2324
static inline int sys_mount(const char *dev_name, const char *dir_name,
2425
const char *type, unsigned long flags,
@@ -788,6 +789,84 @@ static int userns_obj_priv_prog(int mnt_fd, struct token_lsm *lsm_skel)
788789
return 0;
789790
}
790791

792+
static int userns_obj_priv_freplace_setup(int mnt_fd, struct priv_freplace_prog **fr_skel,
793+
struct priv_prog **skel, int *tgt_fd)
794+
{
795+
LIBBPF_OPTS(bpf_object_open_opts, opts);
796+
int err;
797+
char buf[256];
798+
799+
/* use bpf_token_path to provide BPF FS path */
800+
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd);
801+
opts.bpf_token_path = buf;
802+
*skel = priv_prog__open_opts(&opts);
803+
if (!ASSERT_OK_PTR(*skel, "priv_prog__open_opts"))
804+
return -EINVAL;
805+
err = priv_prog__load(*skel);
806+
if (!ASSERT_OK(err, "priv_prog__load"))
807+
return -EINVAL;
808+
809+
*fr_skel = priv_freplace_prog__open_opts(&opts);
810+
if (!ASSERT_OK_PTR(*skel, "priv_freplace_prog__open_opts"))
811+
return -EINVAL;
812+
813+
*tgt_fd = bpf_program__fd((*skel)->progs.xdp_prog1);
814+
return 0;
815+
}
816+
817+
/* Verify that freplace works from user namespace, because bpf token is loaded
818+
* in bpf_object__prepare
819+
*/
820+
static int userns_obj_priv_freplace_prog(int mnt_fd, struct token_lsm *lsm_skel)
821+
{
822+
struct priv_freplace_prog *fr_skel = NULL;
823+
struct priv_prog *skel = NULL;
824+
int err, tgt_fd;
825+
826+
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
827+
if (!ASSERT_OK(err, "setup"))
828+
goto out;
829+
830+
err = bpf_object__prepare(fr_skel->obj);
831+
if (!ASSERT_OK(err, "freplace__prepare"))
832+
goto out;
833+
834+
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
835+
if (!ASSERT_OK(err, "set_attach_target"))
836+
goto out;
837+
838+
err = priv_freplace_prog__load(fr_skel);
839+
ASSERT_OK(err, "priv_freplace_prog__load");
840+
841+
out:
842+
priv_freplace_prog__destroy(fr_skel);
843+
priv_prog__destroy(skel);
844+
return err;
845+
}
846+
847+
/* Verify that replace fails to set attach target from user namespace without bpf token */
848+
static int userns_obj_priv_freplace_prog_fail(int mnt_fd, struct token_lsm *lsm_skel)
849+
{
850+
struct priv_freplace_prog *fr_skel = NULL;
851+
struct priv_prog *skel = NULL;
852+
int err, tgt_fd;
853+
854+
err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd);
855+
if (!ASSERT_OK(err, "setup"))
856+
goto out;
857+
858+
err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1");
859+
if (ASSERT_ERR(err, "attach fails"))
860+
err = 0;
861+
else
862+
err = -EINVAL;
863+
864+
out:
865+
priv_freplace_prog__destroy(fr_skel);
866+
priv_prog__destroy(skel);
867+
return err;
868+
}
869+
791870
/* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command,
792871
* which should cause struct_ops application to fail, as BTF won't be uploaded
793872
* into the kernel, even if STRUCT_OPS programs themselves are allowed
@@ -1004,12 +1083,28 @@ void test_token(void)
10041083
if (test__start_subtest("obj_priv_prog")) {
10051084
struct bpffs_opts opts = {
10061085
.cmds = bit(BPF_PROG_LOAD),
1007-
.progs = bit(BPF_PROG_TYPE_KPROBE),
1086+
.progs = bit(BPF_PROG_TYPE_XDP),
10081087
.attachs = ~0ULL,
10091088
};
10101089

10111090
subtest_userns(&opts, userns_obj_priv_prog);
10121091
}
1092+
if (test__start_subtest("obj_priv_freplace_prog")) {
1093+
struct bpffs_opts opts = {
1094+
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
1095+
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
1096+
.attachs = ~0ULL,
1097+
};
1098+
subtest_userns(&opts, userns_obj_priv_freplace_prog);
1099+
}
1100+
if (test__start_subtest("obj_priv_freplace_prog_fail")) {
1101+
struct bpffs_opts opts = {
1102+
.cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID),
1103+
.progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP),
1104+
.attachs = ~0ULL,
1105+
};
1106+
subtest_userns(&opts, userns_obj_priv_freplace_prog_fail);
1107+
}
10131108
if (test__start_subtest("obj_priv_btf_fail")) {
10141109
struct bpffs_opts opts = {
10151110
/* disallow BTF loading */
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
3+
4+
#include "vmlinux.h"
5+
#include <bpf/bpf_helpers.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
SEC("freplace/xdp_prog1")
10+
int new_xdp_prog2(struct xdp_md *xd)
11+
{
12+
return XDP_DROP;
13+
}

tools/testing/selftests/bpf/progs/priv_prog.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
char _license[] SEC("license") = "GPL";
88

9-
SEC("kprobe")
10-
int kprobe_prog(void *ctx)
9+
SEC("xdp")
10+
int xdp_prog1(struct xdp_md *xdp)
1111
{
12-
return 1;
12+
return XDP_DROP;
1313
}

0 commit comments

Comments
 (0)