[PATCH 12/18] bpf-preload: Implement new registration method for preloading eBPF programs
Roberto Sassu
roberto.sassu at huawei.com
Mon Mar 28 17:50:27 UTC 2022
The current registration method consisting in setting the bpf_preload_ops
global variable is not suitable for preloading multiple eBPF programs, as
each eBPF program would overwrite the global variable with its own method.
Implement a new registration method in two steps. First, introduce
bpf_init_preload_list() to populate at kernel initialization time the new
linked list with an element for each of the desired eBPF programs to
preload.
Second, introduce bpf_preload_set_ops() to allow an eBPF program to set its
preload method in the corresponding item of the linked list. The condition
for a successful registration is that the item in the linked list should
already exist. Return a boolean value to report if registration was
successful or not.
Signed-off-by: Roberto Sassu <roberto.sassu at huawei.com>
---
include/linux/bpf_preload.h | 7 +++
kernel/bpf/inode.c | 107 +++++++++++++++++++++++++++++++++++-
2 files changed, 113 insertions(+), 1 deletion(-)
diff --git a/include/linux/bpf_preload.h b/include/linux/bpf_preload.h
index e604933b3daa..bdbe75c22fcb 100644
--- a/include/linux/bpf_preload.h
+++ b/include/linux/bpf_preload.h
@@ -19,12 +19,19 @@ extern struct bpf_preload_ops *bpf_preload_ops;
int bpf_obj_do_pin_kernel(struct dentry *parent, const char *name, void *raw,
enum bpf_type type);
+bool bpf_preload_set_ops(const char *name, struct module *owner,
+ struct bpf_preload_ops *ops);
#else
static inline int bpf_obj_do_pin_kernel(struct dentry *parent, const char *name,
void *raw, enum bpf_type type)
{
return -EOPNOTSUPP;
}
+
+static inline bool bpf_preload_set_ops(const char *name, struct module *owner,
+ struct bpf_preload_ops *ops)
+{
+}
#endif /*CONFIG_BPF_SYSCALL*/
#endif
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 0a6e83d32360..440ea517cc29 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -22,6 +22,8 @@
#include <linux/bpf_trace.h>
#include <linux/bpf_preload.h>
+static char *bpf_preload_list_str;
+
static void *bpf_any_get(void *raw, enum bpf_type type)
{
switch (type) {
@@ -855,6 +857,100 @@ static struct file_system_type bpf_fs_type = {
.kill_sb = kill_litter_super,
};
+static struct bpf_preload_ops_item *
+bpf_preload_list_lookup_entry(const char *obj_name)
+{
+ struct bpf_preload_ops_item *cur;
+
+ list_for_each_entry(cur, &preload_list, list)
+ if (!strcmp(obj_name, cur->obj_name))
+ return cur;
+
+ return NULL;
+}
+
+static int bpf_preload_list_add_entry(const char *obj_name,
+ struct bpf_preload_ops *ops)
+{
+ struct bpf_preload_ops_item *new;
+
+ if (!*obj_name)
+ return 0;
+
+ new = kzalloc(sizeof(*new), GFP_NOFS);
+ if (!new)
+ return -ENOMEM;
+
+ new->obj_name = kstrdup(obj_name, GFP_NOFS);
+ if (!new->obj_name) {
+ kfree(new);
+ return -ENOMEM;
+ }
+
+ new->ops = ops;
+
+ list_add(&new->list, &preload_list);
+ return 0;
+}
+
+bool bpf_preload_set_ops(const char *obj_name, struct module *owner,
+ struct bpf_preload_ops *ops)
+{
+ struct bpf_preload_ops_item *found_item;
+ bool set = false;
+
+ mutex_lock(&bpf_preload_lock);
+
+ found_item = bpf_preload_list_lookup_entry(obj_name);
+ if (found_item) {
+ if (!found_item->ops ||
+ (found_item->ops && found_item->ops->owner == owner)) {
+ found_item->ops = ops;
+ set = true;
+ }
+ }
+
+ mutex_unlock(&bpf_preload_lock);
+ return set;
+}
+EXPORT_SYMBOL_GPL(bpf_preload_set_ops);
+
+static int __init bpf_init_preload_list(void)
+{
+ char *str_ptr = bpf_preload_list_str, *str_end;
+ struct bpf_preload_ops_item *cur, *tmp;
+ char obj_name[NAME_MAX + 1];
+ int ret;
+
+ while (str_ptr && *str_ptr) {
+ str_end = strchrnul(str_ptr, ',');
+
+ snprintf(obj_name, sizeof(obj_name), "%.*s",
+ (int)(str_end - str_ptr), str_ptr);
+
+ if (!bpf_preload_list_lookup_entry(obj_name)) {
+ ret = bpf_preload_list_add_entry(obj_name, NULL);
+ if (ret)
+ goto out;
+ }
+
+ if (!*str_end)
+ break;
+
+ str_ptr = str_end + 1;
+ }
+
+ return 0;
+out:
+ list_for_each_entry_safe(cur, tmp, &preload_list, list) {
+ list_del(&cur->list);
+ kfree(cur->obj_name);
+ kfree(cur);
+ }
+
+ return ret;
+}
+
static int __init bpf_init(void)
{
int ret;
@@ -864,8 +960,17 @@ static int __init bpf_init(void)
return ret;
ret = register_filesystem(&bpf_fs_type);
- if (ret)
+ if (ret) {
sysfs_remove_mount_point(fs_kobj, "bpf");
+ return ret;
+ }
+
+ ret = bpf_init_preload_list();
+ if (ret) {
+ unregister_filesystem(&bpf_fs_type);
+ sysfs_remove_mount_point(fs_kobj, "bpf");
+ return ret;
+ }
return ret;
}
--
2.32.0
More information about the Linux-security-module-archive
mailing list