Skip to content

Commit b02f072

Browse files
committedMar 17, 2025
Merge branch 'support-freplace-prog-from-user-namespace'
Mykyta Yatsenko says: ==================== Support freplace prog from user namespace From: Mykyta Yatsenko <[email protected]> Freplace programs can't be loaded from user namespace, as bpf_program__set_attach_target() requires searching for target prog BTF, which is locked under CAP_SYS_ADMIN. This patch set enables this use case by: 1. Relaxing capable check in bpf's BPF_BTF_GET_FD_BY_ID, check for CAP_BPF instead of CAP_SYS_ADMIN, support BPF token in attr argument. 2. Pass BPF token around libbpf from bpf_program__set_attach_target() to bpf syscall where capable check is. 3. Validate positive/negative scenarios in selftests This patch set is enabled by the recent libbpf change[1], that introduced bpf_object__prepare() API. Calling bpf_object__prepare() for freplace program before bpf_program__set_attach_target() initializes BPF token, which is then passed to bpf syscall by libbpf. [1] https://lore.kernel.org/all/[email protected]/ ==================== Link: https://patch.msgid.link/[email protected] Signed-off-by: Andrii Nakryiko <[email protected]>
2 parents 812f770 + a024843 commit b02f072

File tree

11 files changed

+160
-17
lines changed

11 files changed

+160
-17
lines changed
 

‎include/uapi/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,7 @@ union bpf_attr {
16521652
};
16531653
__u32 next_id;
16541654
__u32 open_flags;
1655+
__s32 fd_by_id_token_fd;
16551656
};
16561657

16571658
struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */

‎kernel/bpf/syscall.c

+23-4
Original file line numberDiff line numberDiff line change
@@ -4732,6 +4732,8 @@ static int bpf_prog_get_info_by_fd(struct file *file,
47324732
info.recursion_misses = stats.misses;
47334733

47344734
info.verified_insns = prog->aux->verified_insns;
4735+
if (prog->aux->btf)
4736+
info.btf_id = btf_obj_id(prog->aux->btf);
47354737

47364738
if (!bpf_capable()) {
47374739
info.jited_prog_len = 0;
@@ -4878,8 +4880,6 @@ static int bpf_prog_get_info_by_fd(struct file *file,
48784880
}
48794881
}
48804882

4881-
if (prog->aux->btf)
4882-
info.btf_id = btf_obj_id(prog->aux->btf);
48834883
info.attach_btf_id = prog->aux->attach_btf_id;
48844884
if (attach_btf)
48854885
info.attach_btf_obj_id = btf_obj_id(attach_btf);
@@ -5120,15 +5120,34 @@ static int bpf_btf_load(const union bpf_attr *attr, bpfptr_t uattr, __u32 uattr_
51205120
return btf_new_fd(attr, uattr, uattr_size);
51215121
}
51225122

5123-
#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD btf_id
5123+
#define BPF_BTF_GET_FD_BY_ID_LAST_FIELD fd_by_id_token_fd
51245124

51255125
static int bpf_btf_get_fd_by_id(const union bpf_attr *attr)
51265126
{
5127+
struct bpf_token *token = NULL;
5128+
51275129
if (CHECK_ATTR(BPF_BTF_GET_FD_BY_ID))
51285130
return -EINVAL;
51295131

5130-
if (!capable(CAP_SYS_ADMIN))
5132+
if (attr->open_flags & ~BPF_F_TOKEN_FD)
5133+
return -EINVAL;
5134+
5135+
if (attr->open_flags & BPF_F_TOKEN_FD) {
5136+
token = bpf_token_get_from_fd(attr->fd_by_id_token_fd);
5137+
if (IS_ERR(token))
5138+
return PTR_ERR(token);
5139+
if (!bpf_token_allow_cmd(token, BPF_BTF_GET_FD_BY_ID)) {
5140+
bpf_token_put(token);
5141+
token = NULL;
5142+
}
5143+
}
5144+
5145+
if (!bpf_token_capable(token, CAP_SYS_ADMIN)) {
5146+
bpf_token_put(token);
51315147
return -EPERM;
5148+
}
5149+
5150+
bpf_token_put(token);
51325151

51335152
return btf_get_fd_by_id(attr->btf_id);
51345153
}

‎tools/include/uapi/linux/bpf.h

+1
Original file line numberDiff line numberDiff line change
@@ -1652,6 +1652,7 @@ union bpf_attr {
16521652
};
16531653
__u32 next_id;
16541654
__u32 open_flags;
1655+
__s32 fd_by_id_token_fd;
16551656
};
16561657

16571658
struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */

‎tools/lib/bpf/bpf.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ int bpf_map_get_fd_by_id(__u32 id)
10971097
int bpf_btf_get_fd_by_id_opts(__u32 id,
10981098
const struct bpf_get_fd_by_id_opts *opts)
10991099
{
1100-
const size_t attr_sz = offsetofend(union bpf_attr, open_flags);
1100+
const size_t attr_sz = offsetofend(union bpf_attr, fd_by_id_token_fd);
11011101
union bpf_attr attr;
11021102
int fd;
11031103

@@ -1107,6 +1107,7 @@ int bpf_btf_get_fd_by_id_opts(__u32 id,
11071107
memset(&attr, 0, attr_sz);
11081108
attr.btf_id = id;
11091109
attr.open_flags = OPTS_GET(opts, open_flags, 0);
1110+
attr.fd_by_id_token_fd = OPTS_GET(opts, token_fd, 0);
11101111

11111112
fd = sys_bpf_fd(BPF_BTF_GET_FD_BY_ID, &attr, attr_sz);
11121113
return libbpf_err_errno(fd);

‎tools/lib/bpf/bpf.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,10 @@ LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id);
487487
struct bpf_get_fd_by_id_opts {
488488
size_t sz; /* size of this struct for forward/backward compatibility */
489489
__u32 open_flags; /* permissions requested for the operation on fd */
490+
__u32 token_fd;
490491
size_t :0;
491492
};
492-
#define bpf_get_fd_by_id_opts__last_field open_flags
493+
#define bpf_get_fd_by_id_opts__last_field token_fd
493494

494495
LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
495496
LIBBPF_API int bpf_prog_get_fd_by_id_opts(__u32 id,

‎tools/lib/bpf/btf.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -1619,12 +1619,18 @@ struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf)
16191619
return btf;
16201620
}
16211621

1622-
struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf)
1622+
struct btf *btf_load_from_kernel(__u32 id, struct btf *base_btf, int token_fd)
16231623
{
16241624
struct btf *btf;
16251625
int btf_fd;
1626+
LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts);
1627+
1628+
if (token_fd) {
1629+
opts.open_flags |= BPF_F_TOKEN_FD;
1630+
opts.token_fd = token_fd;
1631+
}
16261632

1627-
btf_fd = bpf_btf_get_fd_by_id(id);
1633+
btf_fd = bpf_btf_get_fd_by_id_opts(id, &opts);
16281634
if (btf_fd < 0)
16291635
return libbpf_err_ptr(-errno);
16301636

@@ -1634,6 +1640,11 @@ struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf)
16341640
return libbpf_ptr(btf);
16351641
}
16361642

1643+
struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf)
1644+
{
1645+
return btf_load_from_kernel(id, base_btf, 0);
1646+
}
1647+
16371648
struct btf *btf__load_from_kernel_by_id(__u32 id)
16381649
{
16391650
return btf__load_from_kernel_by_id_split(id, NULL);

‎tools/lib/bpf/libbpf.c

+5-5
Original file line numberDiff line numberDiff line change
@@ -10024,7 +10024,7 @@ int libbpf_find_vmlinux_btf_id(const char *name,
1002410024
return libbpf_err(err);
1002510025
}
1002610026

10027-
static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd)
10027+
static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd, int token_fd)
1002810028
{
1002910029
struct bpf_prog_info info;
1003010030
__u32 info_len = sizeof(info);
@@ -10044,7 +10044,7 @@ static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd)
1004410044
pr_warn("The target program doesn't have BTF\n");
1004510045
goto out;
1004610046
}
10047-
btf = btf__load_from_kernel_by_id(info.btf_id);
10047+
btf = btf_load_from_kernel(info.btf_id, NULL, token_fd);
1004810048
err = libbpf_get_error(btf);
1004910049
if (err) {
1005010050
pr_warn("Failed to get BTF %d of the program: %s\n", info.btf_id, errstr(err));
@@ -10127,7 +10127,7 @@ static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attac
1012710127
pr_warn("prog '%s': attach program FD is not set\n", prog->name);
1012810128
return -EINVAL;
1012910129
}
10130-
err = libbpf_find_prog_btf_id(attach_name, attach_prog_fd);
10130+
err = libbpf_find_prog_btf_id(attach_name, attach_prog_fd, prog->obj->token_fd);
1013110131
if (err < 0) {
1013210132
pr_warn("prog '%s': failed to find BPF program (FD %d) BTF ID for '%s': %s\n",
1013310133
prog->name, attach_prog_fd, attach_name, errstr(err));
@@ -12923,7 +12923,7 @@ struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog,
1292312923
if (target_fd) {
1292412924
LIBBPF_OPTS(bpf_link_create_opts, target_opts);
1292512925

12926-
btf_id = libbpf_find_prog_btf_id(attach_func_name, target_fd);
12926+
btf_id = libbpf_find_prog_btf_id(attach_func_name, target_fd, prog->obj->token_fd);
1292712927
if (btf_id < 0)
1292812928
return libbpf_err_ptr(btf_id);
1292912929

@@ -13744,7 +13744,7 @@ int bpf_program__set_attach_target(struct bpf_program *prog,
1374413744

1374513745
if (attach_prog_fd) {
1374613746
btf_id = libbpf_find_prog_btf_id(attach_func_name,
13747-
attach_prog_fd);
13747+
attach_prog_fd, prog->obj->token_fd);
1374813748
if (btf_id < 0)
1374913749
return libbpf_err(btf_id);
1375013750
} else {

‎tools/lib/bpf/libbpf_internal.h

+1
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
409409
int btf_load_into_kernel(struct btf *btf,
410410
char *log_buf, size_t log_sz, __u32 log_level,
411411
int token_fd);
412+
struct btf *btf_load_from_kernel(__u32 id, struct btf *base_btf, int token_fd);
412413

413414
struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
414415
void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,

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

+96-1
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 */
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

+3-3
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)
Please sign in to comment.