bpf: btf: Add BPF_BTF_LOAD command

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 <kafai@fb.com>
Acked-by: Alexei Starovoitov <ast@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
Martin KaFai Lau
2018-04-18 15:56:01 -07:00
committed by Daniel Borkmann
parent b00b8daec8
commit f56a653c1f
4 changed files with 97 additions and 0 deletions

View File

@@ -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 {
@@ -604,6 +607,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;
@@ -1963,6 +1977,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;
}
@@ -1980,3 +1995,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;
}