[PATCHv6 01/13] bpf: Introduce kfuncs to parser buffer content
Pingfan Liu
piliu at redhat.com
Sun Jan 18 19:24:12 PST 2026
In the security kexec_file_load case, the buffer holding the kernel
image should not be accessible from userspace.
Typically, BPF data flow occurs between user space and kernel space in
either direction. However, the above case presents a unique scenario
where the kernel, instead of a user task, reads data from a file, passes
it to a BPF program for parsing, and finally stores the parsed result.
This requires a mechanism to channel the intermediate data from the BPF
program directly to the kernel. BPF buffer parser kfuncs are introduced
to serve this purpose:
BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE)
BTF_ID_FLAGS(func, bpf_buffer_parser, KF_TRUSTED_ARGS | KF_SLEEPABLE)
where bpf_get_parser_context() and bpf_put_parser_context() manage the
trusted argument, and bpf_buffer_parser() forwards data to a callback
that processes the structured buffer constructed by the BPF program.
Signed-off-by: Pingfan Liu <piliu at redhat.com>
Cc: Alexei Starovoitov <ast at kernel.org>
Cc: Daniel Borkmann <daniel at iogearbox.net>
Cc: David S. Miller <davem at davemloft.net>
Cc: John Fastabend <john.fastabend at gmail.com>
Cc: Andrii Nakryiko <andrii at kernel.org>
Cc: Martin KaFai Lau <martin.lau at linux.dev>
Cc: Eduard Zingerman <eddyz87 at gmail.com>
Cc: Song Liu <song at kernel.org>
Cc: Yonghong Song <yonghong.song at linux.dev>
Cc: KP Singh <kpsingh at kernel.org>
Cc: Stanislav Fomichev <sdf at fomichev.me>
Cc: Hao Luo <haoluo at google.com>
Cc: Jiri Olsa <jolsa at kernel.org>
To: bpf at vger.kernel.org
---
include/linux/bpf.h | 19 ++++
kernel/bpf/Makefile | 3 +
kernel/bpf/bpf_buffer_parser.c | 170 +++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+)
create mode 100644 kernel/bpf/bpf_buffer_parser.c
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index e5be698256d15..25bc1b6b8a600 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -3843,4 +3843,23 @@ static inline int bpf_map_check_op_flags(struct bpf_map *map, u64 flags, u64 all
return 0;
}
+struct bpf_parser_buf {
+ char *buf;
+ int size;
+};
+
+struct bpf_parser_context;
+typedef int (*bpf_parser_handler_t)(struct bpf_parser_context *ctx);
+
+struct bpf_parser_context {
+ struct kref ref;
+ struct hlist_node hash_node;
+ bpf_parser_handler_t func;
+ struct bpf_parser_buf *buf;
+ void *data;
+};
+
+struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t func,
+ void *data);
+void put_bpf_parser_context(struct bpf_parser_context *ctx);
#endif /* _LINUX_BPF_H */
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 232cbc97434db..309b905a81736 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -56,6 +56,9 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
endif
+ifeq ($(CONFIG_KEXEC_BPF),y)
+obj-$(CONFIG_BPF_SYSCALL) += bpf_buffer_parser.o
+endif
CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
diff --git a/kernel/bpf/bpf_buffer_parser.c b/kernel/bpf/bpf_buffer_parser.c
new file mode 100644
index 0000000000000..6acb4b5da71b3
--- /dev/null
+++ b/kernel/bpf/bpf_buffer_parser.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/hashtable.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/vmalloc.h>
+#include <linux/bpf.h>
+
+#define BPF_CONTEXT_HASH_BITS 10
+
+static DEFINE_SPINLOCK(bpf_parser_context_lock);
+static DEFINE_HASHTABLE(bpf_parser_context_map, BPF_CONTEXT_HASH_BITS);
+
+/* Generate a simple hash key from pointer address */
+static inline unsigned int bpf_parser_context_hash_key(struct bpf_parser_context *ctx)
+{
+ return hash_ptr(ctx, BPF_CONTEXT_HASH_BITS);
+}
+
+static void release_bpf_parser_context(struct kref *kref)
+{
+ struct bpf_parser_context *ctx = container_of(kref, struct bpf_parser_context, ref);
+
+ if (!!ctx->buf) {
+ vfree(ctx->buf->buf);
+ kfree(ctx->buf);
+ }
+ spin_lock(&bpf_parser_context_lock);
+ hash_del(&ctx->hash_node);
+ spin_unlock(&bpf_parser_context_lock);
+ kfree(ctx);
+}
+
+struct bpf_parser_context *alloc_bpf_parser_context(bpf_parser_handler_t func,
+ void *data)
+{
+ struct bpf_parser_context *ctx;
+ unsigned int key;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+ ctx->func = func;
+ ctx->data = data;
+ kref_init(&ctx->ref);
+ key = bpf_parser_context_hash_key(ctx);
+ spin_lock(&bpf_parser_context_lock);
+ hash_add(bpf_parser_context_map, &ctx->hash_node, key);
+ spin_unlock(&bpf_parser_context_lock);
+
+ return ctx;
+}
+
+void put_bpf_parser_context(struct bpf_parser_context *ctx)
+{
+ if (!ctx)
+ return;
+ kref_put(&ctx->ref, release_bpf_parser_context);
+}
+
+static struct bpf_parser_context *find_bpf_parser_context(unsigned long id)
+{
+ struct bpf_parser_context *ctx;
+ unsigned int key;
+ int cnt;
+
+ key = bpf_parser_context_hash_key((struct bpf_parser_context *)id);
+ spin_lock(&bpf_parser_context_lock);
+ hash_for_each_possible(bpf_parser_context_map, ctx, hash_node, key) {
+ if (ctx == (struct bpf_parser_context *)id) {
+ cnt = kref_get_unless_zero(&ctx->ref);
+ if (!cnt)
+ ctx = NULL;
+ spin_unlock(&bpf_parser_context_lock);
+ return ctx;
+ }
+ }
+ spin_unlock(&bpf_parser_context_lock);
+
+ return NULL;
+}
+
+__bpf_kfunc_start_defs()
+
+__bpf_kfunc struct bpf_parser_context *bpf_get_parser_context(unsigned long id)
+{
+ struct bpf_parser_context *ctx;
+
+ ctx = find_bpf_parser_context(id);
+
+ return ctx;
+}
+
+__bpf_kfunc void bpf_put_parser_context(struct bpf_parser_context *ctx)
+{
+ put_bpf_parser_context(ctx);
+}
+
+__bpf_kfunc void bpf_parser_context_release_dtor(void *ctx)
+{
+ put_bpf_parser_context(ctx);
+}
+CFI_NOSEAL(bpf_parser_context_release_dtor);
+
+__bpf_kfunc int bpf_buffer_parser(char *buf, int buf_sz,
+ struct bpf_parser_context *context)
+{
+ struct bpf_parser_buf *parser_buf;
+ int ret;
+ char *b;
+
+ if (unlikely(context->func == NULL))
+ return -EINVAL;
+
+ b = __vmalloc(buf_sz, GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+ if (!b)
+ return -ENOMEM;
+ ret = copy_from_kernel_nofault(b, buf, buf_sz);
+ if (!!ret) {
+ vfree(b);
+ return ret;
+ }
+
+ parser_buf = kmalloc(sizeof(struct bpf_parser_buf), GFP_KERNEL);
+ if (!parser_buf) {
+ vfree(b);
+ return -ENOMEM;
+ }
+ parser_buf->buf = b;
+ parser_buf->size = buf_sz;
+ context->buf = parser_buf;
+ ret = context->func(context);
+
+ return ret;
+}
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(buffer_parser_ids)
+BTF_ID_FLAGS(func, bpf_get_parser_context, KF_ACQUIRE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_put_parser_context, KF_RELEASE)
+BTF_ID_FLAGS(func, bpf_buffer_parser, KF_TRUSTED_ARGS | KF_SLEEPABLE)
+BTF_KFUNCS_END(buffer_parser_ids)
+
+static const struct btf_kfunc_id_set buffer_parser_kfunc_set = {
+ .owner = THIS_MODULE,
+ .set = &buffer_parser_ids,
+};
+
+
+BTF_ID_LIST(buffer_parser_dtor_ids)
+BTF_ID(struct, bpf_parser_context)
+BTF_ID(func, bpf_parser_context_release_dtor)
+
+static int __init buffer_parser_kfunc_init(void)
+{
+ int ret;
+ const struct btf_id_dtor_kfunc buffer_parser_dtors[] = {
+ {
+ .btf_id = buffer_parser_dtor_ids[0],
+ .kfunc_btf_id = buffer_parser_dtor_ids[1]
+ },
+ };
+
+ ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING, &buffer_parser_kfunc_set);
+ return ret ?: register_btf_id_dtor_kfuncs(buffer_parser_dtors,
+ ARRAY_SIZE(buffer_parser_dtors),
+ THIS_MODULE);
+}
+
+late_initcall(buffer_parser_kfunc_init);
--
2.49.0
More information about the kexec
mailing list