Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'support-freplace-prog-from-user-namespace'

Mykyta Yatsenko says:

====================
Support freplace prog from user namespace

From: Mykyta Yatsenko <yatsenko@meta.com>

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/20250303135752.158343-1-mykyta.yatsenko5@gmail.com/
====================

Link: https://patch.msgid.link/20250317174039.161275-1-mykyta.yatsenko5@gmail.com
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

+160 -17
+1
include/uapi/linux/bpf.h
··· 1652 1652 }; 1653 1653 __u32 next_id; 1654 1654 __u32 open_flags; 1655 + __s32 fd_by_id_token_fd; 1655 1656 }; 1656 1657 1657 1658 struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+23 -4
kernel/bpf/syscall.c
··· 4732 4732 info.recursion_misses = stats.misses; 4733 4733 4734 4734 info.verified_insns = prog->aux->verified_insns; 4735 + if (prog->aux->btf) 4736 + info.btf_id = btf_obj_id(prog->aux->btf); 4735 4737 4736 4738 if (!bpf_capable()) { 4737 4739 info.jited_prog_len = 0; ··· 4880 4878 } 4881 4879 } 4882 4880 4883 - if (prog->aux->btf) 4884 - info.btf_id = btf_obj_id(prog->aux->btf); 4885 4881 info.attach_btf_id = prog->aux->attach_btf_id; 4886 4882 if (attach_btf) 4887 4883 info.attach_btf_obj_id = btf_obj_id(attach_btf); ··· 5120 5120 return btf_new_fd(attr, uattr, uattr_size); 5121 5121 } 5122 5122 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 5124 5124 5125 5125 static int bpf_btf_get_fd_by_id(const union bpf_attr *attr) 5126 5126 { 5127 + struct bpf_token *token = NULL; 5128 + 5127 5129 if (CHECK_ATTR(BPF_BTF_GET_FD_BY_ID)) 5128 5130 return -EINVAL; 5129 5131 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); 5131 5147 return -EPERM; 5148 + } 5149 + 5150 + bpf_token_put(token); 5132 5151 5133 5152 return btf_get_fd_by_id(attr->btf_id); 5134 5153 }
+1
tools/include/uapi/linux/bpf.h
··· 1652 1652 }; 1653 1653 __u32 next_id; 1654 1654 __u32 open_flags; 1655 + __s32 fd_by_id_token_fd; 1655 1656 }; 1656 1657 1657 1658 struct { /* anonymous struct used by BPF_OBJ_GET_INFO_BY_FD */
+2 -1
tools/lib/bpf/bpf.c
··· 1097 1097 int bpf_btf_get_fd_by_id_opts(__u32 id, 1098 1098 const struct bpf_get_fd_by_id_opts *opts) 1099 1099 { 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); 1101 1101 union bpf_attr attr; 1102 1102 int fd; 1103 1103 ··· 1107 1107 memset(&attr, 0, attr_sz); 1108 1108 attr.btf_id = id; 1109 1109 attr.open_flags = OPTS_GET(opts, open_flags, 0); 1110 + attr.fd_by_id_token_fd = OPTS_GET(opts, token_fd, 0); 1110 1111 1111 1112 fd = sys_bpf_fd(BPF_BTF_GET_FD_BY_ID, &attr, attr_sz); 1112 1113 return libbpf_err_errno(fd);
+2 -1
tools/lib/bpf/bpf.h
··· 487 487 struct bpf_get_fd_by_id_opts { 488 488 size_t sz; /* size of this struct for forward/backward compatibility */ 489 489 __u32 open_flags; /* permissions requested for the operation on fd */ 490 + __u32 token_fd; 490 491 size_t :0; 491 492 }; 492 - #define bpf_get_fd_by_id_opts__last_field open_flags 493 + #define bpf_get_fd_by_id_opts__last_field token_fd 493 494 494 495 LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id); 495 496 LIBBPF_API int bpf_prog_get_fd_by_id_opts(__u32 id,
+13 -2
tools/lib/bpf/btf.c
··· 1619 1619 return btf; 1620 1620 } 1621 1621 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) 1623 1623 { 1624 1624 struct btf *btf; 1625 1625 int btf_fd; 1626 + LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts); 1626 1627 1627 - btf_fd = bpf_btf_get_fd_by_id(id); 1628 + if (token_fd) { 1629 + opts.open_flags |= BPF_F_TOKEN_FD; 1630 + opts.token_fd = token_fd; 1631 + } 1632 + 1633 + btf_fd = bpf_btf_get_fd_by_id_opts(id, &opts); 1628 1634 if (btf_fd < 0) 1629 1635 return libbpf_err_ptr(-errno); 1630 1636 ··· 1638 1632 close(btf_fd); 1639 1633 1640 1634 return libbpf_ptr(btf); 1635 + } 1636 + 1637 + struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf) 1638 + { 1639 + return btf_load_from_kernel(id, base_btf, 0); 1641 1640 } 1642 1641 1643 1642 struct btf *btf__load_from_kernel_by_id(__u32 id)
+5 -5
tools/lib/bpf/libbpf.c
··· 10024 10024 return libbpf_err(err); 10025 10025 } 10026 10026 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) 10028 10028 { 10029 10029 struct bpf_prog_info info; 10030 10030 __u32 info_len = sizeof(info); ··· 10044 10044 pr_warn("The target program doesn't have BTF\n"); 10045 10045 goto out; 10046 10046 } 10047 - btf = btf__load_from_kernel_by_id(info.btf_id); 10047 + btf = btf_load_from_kernel(info.btf_id, NULL, token_fd); 10048 10048 err = libbpf_get_error(btf); 10049 10049 if (err) { 10050 10050 pr_warn("Failed to get BTF %d of the program: %s\n", info.btf_id, errstr(err)); ··· 10127 10127 pr_warn("prog '%s': attach program FD is not set\n", prog->name); 10128 10128 return -EINVAL; 10129 10129 } 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); 10131 10131 if (err < 0) { 10132 10132 pr_warn("prog '%s': failed to find BPF program (FD %d) BTF ID for '%s': %s\n", 10133 10133 prog->name, attach_prog_fd, attach_name, errstr(err)); ··· 12923 12923 if (target_fd) { 12924 12924 LIBBPF_OPTS(bpf_link_create_opts, target_opts); 12925 12925 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); 12927 12927 if (btf_id < 0) 12928 12928 return libbpf_err_ptr(btf_id); 12929 12929 ··· 13744 13744 13745 13745 if (attach_prog_fd) { 13746 13746 btf_id = libbpf_find_prog_btf_id(attach_func_name, 13747 - attach_prog_fd); 13747 + attach_prog_fd, prog->obj->token_fd); 13748 13748 if (btf_id < 0) 13749 13749 return libbpf_err(btf_id); 13750 13750 } else {
+1
tools/lib/bpf/libbpf_internal.h
··· 409 409 int btf_load_into_kernel(struct btf *btf, 410 410 char *log_buf, size_t log_sz, __u32 log_level, 411 411 int token_fd); 412 + struct btf *btf_load_from_kernel(__u32 id, struct btf *base_btf, int token_fd); 412 413 413 414 struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf); 414 415 void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
+96 -1
tools/testing/selftests/bpf/prog_tests/token.c
··· 19 19 #include "priv_prog.skel.h" 20 20 #include "dummy_st_ops_success.skel.h" 21 21 #include "token_lsm.skel.h" 22 + #include "priv_freplace_prog.skel.h" 22 23 23 24 static inline int sys_mount(const char *dev_name, const char *dir_name, 24 25 const char *type, unsigned long flags, ··· 789 788 return 0; 790 789 } 791 790 791 + static int userns_obj_priv_freplace_setup(int mnt_fd, struct priv_freplace_prog **fr_skel, 792 + struct priv_prog **skel, int *tgt_fd) 793 + { 794 + LIBBPF_OPTS(bpf_object_open_opts, opts); 795 + int err; 796 + char buf[256]; 797 + 798 + /* use bpf_token_path to provide BPF FS path */ 799 + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); 800 + opts.bpf_token_path = buf; 801 + *skel = priv_prog__open_opts(&opts); 802 + if (!ASSERT_OK_PTR(*skel, "priv_prog__open_opts")) 803 + return -EINVAL; 804 + err = priv_prog__load(*skel); 805 + if (!ASSERT_OK(err, "priv_prog__load")) 806 + return -EINVAL; 807 + 808 + *fr_skel = priv_freplace_prog__open_opts(&opts); 809 + if (!ASSERT_OK_PTR(*skel, "priv_freplace_prog__open_opts")) 810 + return -EINVAL; 811 + 812 + *tgt_fd = bpf_program__fd((*skel)->progs.xdp_prog1); 813 + return 0; 814 + } 815 + 816 + /* Verify that freplace works from user namespace, because bpf token is loaded 817 + * in bpf_object__prepare 818 + */ 819 + static int userns_obj_priv_freplace_prog(int mnt_fd, struct token_lsm *lsm_skel) 820 + { 821 + struct priv_freplace_prog *fr_skel = NULL; 822 + struct priv_prog *skel = NULL; 823 + int err, tgt_fd; 824 + 825 + err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd); 826 + if (!ASSERT_OK(err, "setup")) 827 + goto out; 828 + 829 + err = bpf_object__prepare(fr_skel->obj); 830 + if (!ASSERT_OK(err, "freplace__prepare")) 831 + goto out; 832 + 833 + err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1"); 834 + if (!ASSERT_OK(err, "set_attach_target")) 835 + goto out; 836 + 837 + err = priv_freplace_prog__load(fr_skel); 838 + ASSERT_OK(err, "priv_freplace_prog__load"); 839 + 840 + out: 841 + priv_freplace_prog__destroy(fr_skel); 842 + priv_prog__destroy(skel); 843 + return err; 844 + } 845 + 846 + /* Verify that replace fails to set attach target from user namespace without bpf token */ 847 + static int userns_obj_priv_freplace_prog_fail(int mnt_fd, struct token_lsm *lsm_skel) 848 + { 849 + struct priv_freplace_prog *fr_skel = NULL; 850 + struct priv_prog *skel = NULL; 851 + int err, tgt_fd; 852 + 853 + err = userns_obj_priv_freplace_setup(mnt_fd, &fr_skel, &skel, &tgt_fd); 854 + if (!ASSERT_OK(err, "setup")) 855 + goto out; 856 + 857 + err = bpf_program__set_attach_target(fr_skel->progs.new_xdp_prog2, tgt_fd, "xdp_prog1"); 858 + if (ASSERT_ERR(err, "attach fails")) 859 + err = 0; 860 + else 861 + err = -EINVAL; 862 + 863 + out: 864 + priv_freplace_prog__destroy(fr_skel); 865 + priv_prog__destroy(skel); 866 + return err; 867 + } 868 + 792 869 /* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command, 793 870 * which should cause struct_ops application to fail, as BTF won't be uploaded 794 871 * into the kernel, even if STRUCT_OPS programs themselves are allowed ··· 1083 1004 if (test__start_subtest("obj_priv_prog")) { 1084 1005 struct bpffs_opts opts = { 1085 1006 .cmds = bit(BPF_PROG_LOAD), 1086 - .progs = bit(BPF_PROG_TYPE_KPROBE), 1007 + .progs = bit(BPF_PROG_TYPE_XDP), 1087 1008 .attachs = ~0ULL, 1088 1009 }; 1089 1010 1090 1011 subtest_userns(&opts, userns_obj_priv_prog); 1012 + } 1013 + if (test__start_subtest("obj_priv_freplace_prog")) { 1014 + struct bpffs_opts opts = { 1015 + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID), 1016 + .progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP), 1017 + .attachs = ~0ULL, 1018 + }; 1019 + subtest_userns(&opts, userns_obj_priv_freplace_prog); 1020 + } 1021 + if (test__start_subtest("obj_priv_freplace_prog_fail")) { 1022 + struct bpffs_opts opts = { 1023 + .cmds = bit(BPF_BTF_LOAD) | bit(BPF_PROG_LOAD) | bit(BPF_BTF_GET_FD_BY_ID), 1024 + .progs = bit(BPF_PROG_TYPE_EXT) | bit(BPF_PROG_TYPE_XDP), 1025 + .attachs = ~0ULL, 1026 + }; 1027 + subtest_userns(&opts, userns_obj_priv_freplace_prog_fail); 1091 1028 } 1092 1029 if (test__start_subtest("obj_priv_btf_fail")) { 1093 1030 struct bpffs_opts opts = {
+13
tools/testing/selftests/bpf/progs/priv_freplace_prog.c
··· 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 + }
+3 -3
tools/testing/selftests/bpf/progs/priv_prog.c
··· 6 6 7 7 char _license[] SEC("license") = "GPL"; 8 8 9 - SEC("kprobe") 10 - int kprobe_prog(void *ctx) 9 + SEC("xdp") 10 + int xdp_prog1(struct xdp_md *xdp) 11 11 { 12 - return 1; 12 + return XDP_DROP; 13 13 }