This patch adds a BPF_BTF_LOAD command which
1) loads and verifies the BTF (implemented in earlier patches)
2) returns a BTF fd to userspace.  In the next patch, the
   BTF fd can be specified during BPF_MAP_CREATE.

It currently limits to CAP_SYS_ADMIN.

Signed-off-by: Martin KaFai Lau <ka...@fb.com>
Acked-by: Alexei Starovoitov <a...@fb.com>
---
 include/linux/btf.h      |  4 +++
 include/uapi/linux/bpf.h |  9 +++++++
 kernel/bpf/btf.c         | 67 ++++++++++++++++++++++++++++++++++++++++++++++++
 kernel/bpf/syscall.c     | 17 ++++++++++++
 4 files changed, 97 insertions(+)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index d8bdab0280ba..a7c7072535ea 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -8,7 +8,11 @@
 
 struct btf;
 struct btf_type;
+union bpf_attr;
 
+void btf_put(struct btf *btf);
+int btf_new_fd(const union bpf_attr *attr);
+struct btf *btf_get_by_fd(int fd);
 /* Figure out the size of a type_id.  If type_id is a modifier
  * (e.g. const), it will be resolved to find out the type with size.
  *
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index c5ec89732a8d..c7d75f18521b 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -95,6 +95,7 @@ enum bpf_cmd {
        BPF_OBJ_GET_INFO_BY_FD,
        BPF_PROG_QUERY,
        BPF_RAW_TRACEPOINT_OPEN,
+       BPF_BTF_LOAD,
 };
 
 enum bpf_map_type {
@@ -363,6 +364,14 @@ union bpf_attr {
                __u64 name;
                __u32 prog_fd;
        } raw_tracepoint;
+
+       struct { /* anonymous struct for BPF_BTF_LOAD */
+               __aligned_u64   btf;
+               __aligned_u64   btf_log_buf;
+               __u32           btf_size;
+               __u32           btf_log_size;
+               __u32           btf_log_level;
+       };
 } __attribute__((aligned(8)));
 
 /* BPF helper function descriptions:
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 15bfca419231..45af787ceddf 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -7,6 +7,8 @@
 #include <linux/compiler.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
+#include <linux/anon_inodes.h>
+#include <linux/file.h>
 #include <linux/uaccess.h>
 #include <linux/kernel.h>
 #include <linux/bpf_verifier.h>
@@ -190,6 +192,7 @@ struct btf {
        u32 nr_types;
        u32 types_size;
        u32 data_size;
+       refcount_t refcnt;
 };
 
 enum verifier_phase {
@@ -607,6 +610,17 @@ static void btf_free(struct btf *btf)
        kfree(btf);
 }
 
+static void btf_get(struct btf *btf)
+{
+       refcount_inc(&btf->refcnt);
+}
+
+void btf_put(struct btf *btf)
+{
+       if (btf && refcount_dec_and_test(&btf->refcnt))
+               btf_free(btf);
+}
+
 static int env_resolve_init(struct btf_verifier_env *env)
 {
        struct btf *btf = env->btf;
@@ -1992,6 +2006,7 @@ static struct btf *btf_parse(void __user *btf_data, u32 
btf_data_size,
 
        if (!err) {
                btf_verifier_env_free(env);
+               btf_get(btf);
                return btf;
        }
 
@@ -2009,3 +2024,55 @@ void btf_type_seq_show(const struct btf *btf, u32 
type_id, void *obj,
 
        btf_type_ops(t)->seq_show(btf, t, type_id, obj, 0, m);
 }
+
+static int btf_release(struct inode *inode, struct file *filp)
+{
+       btf_put(filp->private_data);
+       return 0;
+}
+
+static const struct file_operations btf_fops = {
+       .release        = btf_release,
+};
+
+int btf_new_fd(const union bpf_attr *attr)
+{
+       struct btf *btf;
+       int fd;
+
+       btf = btf_parse(u64_to_user_ptr(attr->btf),
+                       attr->btf_size, attr->btf_log_level,
+                       u64_to_user_ptr(attr->btf_log_buf),
+                       attr->btf_log_size);
+       if (IS_ERR(btf))
+               return PTR_ERR(btf);
+
+       fd = anon_inode_getfd("btf", &btf_fops, btf,
+                             O_RDONLY | O_CLOEXEC);
+       if (fd < 0)
+               btf_put(btf);
+
+       return fd;
+}
+
+struct btf *btf_get_by_fd(int fd)
+{
+       struct btf *btf;
+       struct fd f;
+
+       f = fdget(fd);
+
+       if (!f.file)
+               return ERR_PTR(-EBADF);
+
+       if (f.file->f_op != &btf_fops) {
+               fdput(f);
+               return ERR_PTR(-EINVAL);
+       }
+
+       btf = f.file->private_data;
+       btf_get(btf);
+       fdput(f);
+
+       return btf;
+}
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 4ca46df19c9a..cd8ebadc66eb 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -11,6 +11,7 @@
  */
 #include <linux/bpf.h>
 #include <linux/bpf_trace.h>
+#include <linux/btf.h>
 #include <linux/syscalls.h>
 #include <linux/slab.h>
 #include <linux/sched/signal.h>
@@ -2023,6 +2024,19 @@ static int bpf_obj_get_info_by_fd(const union bpf_attr 
*attr,
        return err;
 }
 
+#define BPF_BTF_LOAD_LAST_FIELD btf_log_level
+
+static int bpf_btf_load(const union bpf_attr *attr)
+{
+       if (CHECK_ATTR(BPF_BTF_LOAD))
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       return btf_new_fd(attr);
+}
+
 SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, 
size)
 {
        union bpf_attr attr = {};
@@ -2103,6 +2117,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, 
uattr, unsigned int, siz
        case BPF_RAW_TRACEPOINT_OPEN:
                err = bpf_raw_tracepoint_open(&attr);
                break;
+       case BPF_BTF_LOAD:
+               err = bpf_btf_load(&attr);
+               break;
        default:
                err = -EINVAL;
                break;
-- 
2.9.5

Reply via email to