bpf: Allow to specify kernel module BTFs when attaching BPF programs
authorAndrii Nakryiko <andrii@kernel.org>
Thu, 3 Dec 2020 20:46:30 +0000 (12:46 -0800)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 4 Dec 2020 01:38:21 +0000 (17:38 -0800)
Add ability for user-space programs to specify non-vmlinux BTF when attaching
BTF-powered BPF programs: raw_tp, fentry/fexit/fmod_ret, LSM, etc. For this,
attach_prog_fd (now with the alias name attach_btf_obj_fd) should specify FD
of a module or vmlinux BTF object. For backwards compatibility reasons,
0 denotes vmlinux BTF. Only kernel BTF (vmlinux or module) can be specified.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20201203204634.1325171-11-andrii@kernel.org
include/linux/btf.h
include/uapi/linux/bpf.h
kernel/bpf/btf.c
kernel/bpf/syscall.c
tools/include/uapi/linux/bpf.h

index fb608e4de076e2a16839c489855e0cdd821bad75..4c200f5d242be603891e3264ccfc364d73ab8a5f 100644 (file)
@@ -90,6 +90,7 @@ int btf_type_snprintf_show(const struct btf *btf, u32 type_id, void *obj,
 
 int btf_get_fd_by_id(u32 id);
 u32 btf_obj_id(const struct btf *btf);
+bool btf_is_kernel(const struct btf *btf);
 bool btf_member_is_reg_int(const struct btf *btf, const struct btf_type *s,
                           const struct btf_member *m,
                           u32 expected_offset, u32 expected_size);
index c3458ec1f30a4ae59ad78e9548601d66168d0dba..1233f14f659f208aef4ca1361d30aeb46bf306ef 100644 (file)
@@ -557,7 +557,12 @@ union bpf_attr {
                __aligned_u64   line_info;      /* line info */
                __u32           line_info_cnt;  /* number of bpf_line_info records */
                __u32           attach_btf_id;  /* in-kernel BTF type id to attach to */
-               __u32           attach_prog_fd; /* 0 to attach to vmlinux */
+               union {
+                       /* valid prog_fd to attach to bpf prog */
+                       __u32           attach_prog_fd;
+                       /* or valid module BTF object fd or 0 to attach to vmlinux */
+                       __u32           attach_btf_obj_fd;
+               };
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */
index 7a19bf5bfe97b3db1bc7fcd31212b098eb811bb2..8d6bdb4f4d61819c873a437a8d3ef50eebe767fc 100644 (file)
@@ -5738,6 +5738,11 @@ u32 btf_obj_id(const struct btf *btf)
        return btf->id;
 }
 
+bool btf_is_kernel(const struct btf *btf)
+{
+       return btf->kernel_btf;
+}
+
 static int btf_id_cmp_func(const void *a, const void *b)
 {
        const int *pa = a, *pb = b;
index 184204169949597eac59dbb572ecba3bb2241328..0cd3cc2af9c14071ca1273ff1b2a1a8d79b0fda1 100644 (file)
@@ -1926,12 +1926,16 @@ static void bpf_prog_load_fixup_attach_type(union bpf_attr *attr)
 static int
 bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
                           enum bpf_attach_type expected_attach_type,
-                          u32 btf_id, u32 prog_fd)
+                          struct btf *attach_btf, u32 btf_id,
+                          struct bpf_prog *dst_prog)
 {
        if (btf_id) {
                if (btf_id > BTF_MAX_TYPE)
                        return -EINVAL;
 
+               if (!attach_btf && !dst_prog)
+                       return -EINVAL;
+
                switch (prog_type) {
                case BPF_PROG_TYPE_TRACING:
                case BPF_PROG_TYPE_LSM:
@@ -1943,7 +1947,10 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
                }
        }
 
-       if (prog_fd && prog_type != BPF_PROG_TYPE_TRACING &&
+       if (attach_btf && (!btf_id || dst_prog))
+               return -EINVAL;
+
+       if (dst_prog && prog_type != BPF_PROG_TYPE_TRACING &&
            prog_type != BPF_PROG_TYPE_EXT)
                return -EINVAL;
 
@@ -2060,7 +2067,8 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
 static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
 {
        enum bpf_prog_type type = attr->prog_type;
-       struct bpf_prog *prog;
+       struct bpf_prog *prog, *dst_prog = NULL;
+       struct btf *attach_btf = NULL;
        int err;
        char license[128];
        bool is_gpl;
@@ -2102,44 +2110,56 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
        if (is_perfmon_prog_type(type) && !perfmon_capable())
                return -EPERM;
 
+       /* attach_prog_fd/attach_btf_obj_fd can specify fd of either bpf_prog
+        * or btf, we need to check which one it is
+        */
+       if (attr->attach_prog_fd) {
+               dst_prog = bpf_prog_get(attr->attach_prog_fd);
+               if (IS_ERR(dst_prog)) {
+                       dst_prog = NULL;
+                       attach_btf = btf_get_by_fd(attr->attach_btf_obj_fd);
+                       if (IS_ERR(attach_btf))
+                               return -EINVAL;
+                       if (!btf_is_kernel(attach_btf)) {
+                               btf_put(attach_btf);
+                               return -EINVAL;
+                       }
+               }
+       } else if (attr->attach_btf_id) {
+               /* fall back to vmlinux BTF, if BTF type ID is specified */
+               attach_btf = bpf_get_btf_vmlinux();
+               if (IS_ERR(attach_btf))
+                       return PTR_ERR(attach_btf);
+               if (!attach_btf)
+                       return -EINVAL;
+               btf_get(attach_btf);
+       }
+
        bpf_prog_load_fixup_attach_type(attr);
        if (bpf_prog_load_check_attach(type, attr->expected_attach_type,
-                                      attr->attach_btf_id,
-                                      attr->attach_prog_fd))
+                                      attach_btf, attr->attach_btf_id,
+                                      dst_prog)) {
+               if (dst_prog)
+                       bpf_prog_put(dst_prog);
+               if (attach_btf)
+                       btf_put(attach_btf);
                return -EINVAL;
+       }
 
        /* plain bpf_prog allocation */
        prog = bpf_prog_alloc(bpf_prog_size(attr->insn_cnt), GFP_USER);
-       if (!prog)
+       if (!prog) {
+               if (dst_prog)
+                       bpf_prog_put(dst_prog);
+               if (attach_btf)
+                       btf_put(attach_btf);
                return -ENOMEM;
+       }
 
        prog->expected_attach_type = attr->expected_attach_type;
+       prog->aux->attach_btf = attach_btf;
        prog->aux->attach_btf_id = attr->attach_btf_id;
-
-       if (attr->attach_btf_id && !attr->attach_prog_fd) {
-               struct btf *btf;
-
-               btf = bpf_get_btf_vmlinux();
-               if (IS_ERR(btf))
-                       return PTR_ERR(btf);
-               if (!btf)
-                       return -EINVAL;
-
-               btf_get(btf);
-               prog->aux->attach_btf = btf;
-       }
-
-       if (attr->attach_prog_fd) {
-               struct bpf_prog *dst_prog;
-
-               dst_prog = bpf_prog_get(attr->attach_prog_fd);
-               if (IS_ERR(dst_prog)) {
-                       err = PTR_ERR(dst_prog);
-                       goto free_prog;
-               }
-               prog->aux->dst_prog = dst_prog;
-       }
-
+       prog->aux->dst_prog = dst_prog;
        prog->aux->offload_requested = !!attr->prog_ifindex;
        prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE;
 
index c3458ec1f30a4ae59ad78e9548601d66168d0dba..1233f14f659f208aef4ca1361d30aeb46bf306ef 100644 (file)
@@ -557,7 +557,12 @@ union bpf_attr {
                __aligned_u64   line_info;      /* line info */
                __u32           line_info_cnt;  /* number of bpf_line_info records */
                __u32           attach_btf_id;  /* in-kernel BTF type id to attach to */
-               __u32           attach_prog_fd; /* 0 to attach to vmlinux */
+               union {
+                       /* valid prog_fd to attach to bpf prog */
+                       __u32           attach_prog_fd;
+                       /* or valid module BTF object fd or 0 to attach to vmlinux */
+                       __u32           attach_btf_obj_fd;
+               };
        };
 
        struct { /* anonymous struct used by BPF_OBJ_* commands */