[RFC PATCH v4 26/36] WIP: bpf: Add fuse_ops struct_op programs

From: Daniel Rosenberg
Date: Thu Mar 28 2024 - 22:04:10 EST


This introduces a new struct_op type: fuse_ops. This program set
provides pre and post filters to run around fuse-bpf calls that act
directly on the lower filesystem.

The inputs are either fixed structures, or struct fuse_buffer's.

These programs are not permitted to make any changes to these fuse_buffers
unless they create a dynptr wrapper using the supplied kfunc helpers.

Fuse_buffers maintain additional state information that FUSE uses to
manage memory and determine if additional set up or checks are needed.

Signed-off-by: Daniel Rosenberg <drosen@xxxxxxxxxx>
---
include/linux/bpf_fuse.h | 189 +++++++++++
kernel/bpf/Makefile | 4 +
kernel/bpf/bpf_fuse.c | 716 +++++++++++++++++++++++++++++++++++++++
kernel/bpf/btf.c | 1 +
kernel/bpf/verifier.c | 10 +-
5 files changed, 919 insertions(+), 1 deletion(-)
create mode 100644 kernel/bpf/bpf_fuse.c

diff --git a/include/linux/bpf_fuse.h b/include/linux/bpf_fuse.h
index ce8b1b347496..780a7889aea2 100644
--- a/include/linux/bpf_fuse.h
+++ b/include/linux/bpf_fuse.h
@@ -30,6 +30,8 @@ struct fuse_buffer {
#define BPF_FUSE_MODIFIED (1 << 3) // The helper function allowed writes to the buffer
#define BPF_FUSE_ALLOCATED (1 << 4) // The helper function allocated the buffer

+extern void *bpf_fuse_get_writeable(struct fuse_buffer *arg, u64 size, bool copy);
+
/*
* BPF Fuse Args
*
@@ -81,4 +83,191 @@ static inline unsigned bpf_fuse_arg_size(const struct bpf_fuse_arg *arg)
return arg->is_buffer ? arg->buffer->size : arg->size;
}

+struct fuse_ops {
+ uint32_t (*open_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_open_in *in);
+ uint32_t (*open_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_open_in *in,
+ struct fuse_open_out *out);
+
+ uint32_t (*opendir_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_open_in *in);
+ uint32_t (*opendir_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_open_in *in,
+ struct fuse_open_out *out);
+
+ uint32_t (*create_open_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_create_in *in, struct fuse_buffer *name);
+ uint32_t (*create_open_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_create_in *in, const struct fuse_buffer *name,
+ struct fuse_entry_out *entry_out, struct fuse_open_out *out);
+
+ uint32_t (*release_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_release_in *in);
+ uint32_t (*release_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_release_in *in);
+
+ uint32_t (*releasedir_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_release_in *in);
+ uint32_t (*releasedir_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_release_in *in);
+
+ uint32_t (*flush_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_flush_in *in);
+ uint32_t (*flush_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_flush_in *in);
+
+ uint32_t (*lseek_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_lseek_in *in);
+ uint32_t (*lseek_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_lseek_in *in,
+ struct fuse_lseek_out *out);
+
+ uint32_t (*copy_file_range_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_copy_file_range_in *in);
+ uint32_t (*copy_file_range_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_copy_file_range_in *in,
+ struct fuse_write_out *out);
+
+ uint32_t (*fsync_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fsync_in *in);
+ uint32_t (*fsync_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fsync_in *in);
+
+ uint32_t (*dir_fsync_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fsync_in *in);
+ uint32_t (*dir_fsync_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fsync_in *in);
+
+ uint32_t (*getxattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getxattr_in *in, struct fuse_buffer *name);
+ // if in->size > 0, use value. If in->size == 0, use out.
+ uint32_t (*getxattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getxattr_in *in, const struct fuse_buffer *name,
+ struct fuse_buffer *value, struct fuse_getxattr_out *out);
+
+ uint32_t (*listxattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getxattr_in *in);
+ // if in->size > 0, use value. If in->size == 0, use out.
+ uint32_t (*listxattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getxattr_in *in,
+ struct fuse_buffer *value, struct fuse_getxattr_out *out);
+
+ uint32_t (*setxattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_setxattr_in *in, struct fuse_buffer *name,
+ struct fuse_buffer *value);
+ uint32_t (*setxattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_setxattr_in *in, const struct fuse_buffer *name,
+ const struct fuse_buffer *value);
+
+ uint32_t (*removexattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name);
+ uint32_t (*removexattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name);
+
+ /* Read and Write iter will likely undergo some sort of change/addition to handle changing
+ * the data buffer passed in/out. */
+ uint32_t (*read_iter_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_read_in *in);
+ uint32_t (*read_iter_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_read_in *in,
+ struct fuse_read_iter_out *out);
+
+ uint32_t (*write_iter_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_write_in *in);
+ uint32_t (*write_iter_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_write_in *in,
+ struct fuse_write_iter_out *out);
+
+ uint32_t (*file_fallocate_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fallocate_in *in);
+ uint32_t (*file_fallocate_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fallocate_in *in);
+
+ uint32_t (*lookup_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name);
+ uint32_t (*lookup_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name,
+ struct fuse_entry_out *out, struct fuse_buffer *entries);
+
+ uint32_t (*mknod_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_mknod_in *in, struct fuse_buffer *name);
+ uint32_t (*mknod_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_mknod_in *in, const struct fuse_buffer *name);
+
+ uint32_t (*mkdir_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_mkdir_in *in, struct fuse_buffer *name);
+ uint32_t (*mkdir_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_mkdir_in *in, const struct fuse_buffer *name);
+
+ uint32_t (*rmdir_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name);
+ uint32_t (*rmdir_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name);
+
+ uint32_t (*rename2_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_rename2_in *in, struct fuse_buffer *old_name,
+ struct fuse_buffer *new_name);
+ uint32_t (*rename2_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_rename2_in *in, const struct fuse_buffer *old_name,
+ const struct fuse_buffer *new_name);
+
+ uint32_t (*rename_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_rename_in *in, struct fuse_buffer *old_name,
+ struct fuse_buffer *new_name);
+ uint32_t (*rename_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_rename_in *in, const struct fuse_buffer *old_name,
+ const struct fuse_buffer *new_name);
+
+ uint32_t (*unlink_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name);
+ uint32_t (*unlink_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name);
+
+ uint32_t (*link_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_link_in *in, struct fuse_buffer *name);
+ uint32_t (*link_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_link_in *in, const struct fuse_buffer *name);
+
+ uint32_t (*getattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getattr_in *in);
+ uint32_t (*getattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getattr_in *in,
+ struct fuse_attr_out *out);
+
+ uint32_t (*setattr_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_setattr_in *in);
+ uint32_t (*setattr_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_setattr_in *in,
+ struct fuse_attr_out *out);
+
+ uint32_t (*statfs_prefilter)(const struct bpf_fuse_meta_info *meta);
+ uint32_t (*statfs_postfilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_statfs_out *out);
+
+ //TODO: This does not allow doing anything with path
+ uint32_t (*get_link_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name);
+ uint32_t (*get_link_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name);
+
+ uint32_t (*symlink_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name, struct fuse_buffer *path);
+ uint32_t (*symlink_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name, const struct fuse_buffer *path);
+
+ uint32_t (*readdir_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_read_in *in);
+ uint32_t (*readdir_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_read_in *in,
+ struct fuse_read_out *out, struct fuse_buffer *buffer);
+
+ uint32_t (*access_prefilter)(const struct bpf_fuse_meta_info *meta,
+ struct fuse_access_in *in);
+ uint32_t (*access_postfilter)(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_access_in *in);
+
+ char name[BPF_FUSE_NAME_MAX];
+};
+
#endif /* _BPF_FUSE_H */
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 4ce95acfcaa7..1fc22dfc8bee 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -46,3 +46,7 @@ obj-$(CONFIG_BPF_PRELOAD) += preload/
obj-$(CONFIG_BPF_SYSCALL) += relo_core.o
$(obj)/relo_core.o: $(srctree)/tools/lib/bpf/relo_core.c FORCE
$(call if_changed_rule,cc_o_c)
+
+ifeq ($(CONFIG_FUSE_BPF),y)
+obj-$(CONFIG_BPF_SYSCALL) += bpf_fuse.o
+endif
diff --git a/kernel/bpf/bpf_fuse.c b/kernel/bpf/bpf_fuse.c
new file mode 100644
index 000000000000..7ae93b5230c7
--- /dev/null
+++ b/kernel/bpf/bpf_fuse.c
@@ -0,0 +1,716 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021 Google LLC
+
+#include <linux/filter.h>
+#include <linux/bpf.h>
+#include <linux/bpf_fuse.h>
+#include <linux/bpf_verifier.h>
+#include <linux/btf.h>
+
+void *bpf_fuse_get_writeable(struct fuse_buffer *arg, u64 size, bool copy)
+{
+ void *writeable_val;
+
+ if (arg->flags & BPF_FUSE_IMMUTABLE)
+ return 0;
+
+ if (size <= arg->size &&
+ (!(arg->flags & BPF_FUSE_MUST_ALLOCATE) ||
+ (arg->flags & BPF_FUSE_ALLOCATED))) {
+ if (arg->flags & BPF_FUSE_VARIABLE_SIZE)
+ arg->size = size;
+ arg->flags |= BPF_FUSE_MODIFIED;
+ return arg->data;
+ }
+ /* Variable sized arrays must stay below max size. If the buffer must be fixed size,
+ * don't change the allocated size. Verifier will enforce requested size for accesses
+ */
+ if (arg->flags & BPF_FUSE_VARIABLE_SIZE) {
+ if (size > arg->max_size)
+ return 0;
+ } else {
+ if (size > arg->size)
+ return 0;
+ size = arg->size;
+ }
+
+ if (size != arg->size && size > arg->max_size)
+ return 0;
+
+ /* If our buffer is big enough, just adjust size */
+ if (size <= arg->alloc_size) {
+ if (!copy)
+ arg->size = size;
+ arg->flags |= BPF_FUSE_MODIFIED;
+ return arg->data;
+ }
+
+ writeable_val = kzalloc(size, GFP_KERNEL);
+ if (!writeable_val)
+ return 0;
+
+ arg->alloc_size = size;
+ /* If we're copying the buffer, assume the same amount is used. If that isn't the case,
+ * caller must change size. Otherwise, assume entirety of new buffer is used.
+ */
+ if (copy)
+ memcpy(writeable_val, arg->data, (arg->size > size) ? size : arg->size);
+ else
+ arg->size = size;
+
+ if (arg->flags & BPF_FUSE_ALLOCATED)
+ kfree(arg->data);
+ arg->data = writeable_val;
+
+ arg->flags |= BPF_FUSE_ALLOCATED | BPF_FUSE_MODIFIED;
+
+ return arg->data;
+}
+EXPORT_SYMBOL(bpf_fuse_get_writeable);
+
+__bpf_kfunc_start_defs();
+__bpf_kfunc void bpf_fuse_get_rw_dynptr(struct fuse_buffer *buffer, struct bpf_dynptr_kern *dynptr__uninit, u64 size, bool copy)
+{
+ buffer->data = bpf_fuse_get_writeable(buffer, size, copy);
+ bpf_dynptr_init(dynptr__uninit, buffer->data, BPF_DYNPTR_TYPE_LOCAL, 0, buffer->size);
+}
+
+__bpf_kfunc void bpf_fuse_get_ro_dynptr(const struct fuse_buffer *buffer, struct bpf_dynptr_kern *dynptr__uninit)
+{
+ bpf_dynptr_init(dynptr__uninit, buffer->data, BPF_DYNPTR_TYPE_LOCAL, 0, buffer->size);
+ bpf_dynptr_set_rdonly(dynptr__uninit);
+}
+
+__bpf_kfunc uint32_t bpf_fuse_return_len(struct fuse_buffer *buffer)
+{
+ return buffer->size;
+}
+__diag_pop();
+__bpf_kfunc_end_defs();
+BTF_KFUNCS_START(fuse_kfunc_set)
+BTF_ID_FLAGS(func, bpf_fuse_get_rw_dynptr)
+BTF_ID_FLAGS(func, bpf_fuse_get_ro_dynptr)
+BTF_ID_FLAGS(func, bpf_fuse_return_len)
+BTF_KFUNCS_END(fuse_kfunc_set)
+
+static const struct btf_kfunc_id_set bpf_fuse_kfunc_set = {
+ .owner = THIS_MODULE,
+ .set = &fuse_kfunc_set,
+};
+
+static const struct bpf_func_proto *bpf_fuse_get_func_proto(enum bpf_func_id func_id,
+ const struct bpf_prog *prog)
+{
+ switch (func_id) {
+ default:
+ return bpf_base_func_proto(func_id, prog);
+ }
+}
+
+static bool bpf_fuse_is_valid_access(int off, int size,
+ enum bpf_access_type type,
+ const struct bpf_prog *prog,
+ struct bpf_insn_access_aux *info)
+{
+ return bpf_tracing_btf_ctx_access(off, size, type, prog, info);
+}
+
+const struct btf_type *fuse_buffer_struct_type;
+
+static int bpf_fuse_btf_struct_access(struct bpf_verifier_log *log,
+ const struct bpf_reg_state *reg,
+ int off, int size)
+{
+ const struct btf_type *t;
+
+ t = btf_type_by_id(reg->btf, reg->btf_id);
+ if (t == fuse_buffer_struct_type) {
+ bpf_log(log,
+ "direct access to fuse_buffer is disallowed\n");
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static const struct bpf_verifier_ops bpf_fuse_verifier_ops = {
+ .get_func_proto = bpf_fuse_get_func_proto,
+ .is_valid_access = bpf_fuse_is_valid_access,
+ .btf_struct_access = bpf_fuse_btf_struct_access,
+};
+
+static int bpf_fuse_check_member(const struct btf_type *t,
+ const struct btf_member *member,
+ const struct bpf_prog *prog)
+{
+ //if (is_unsupported(__btf_member_bit_offset(t, member) / 8))
+ // return -ENOTSUPP;
+ return 0;
+}
+
+static int bpf_fuse_init_member(const struct btf_type *t,
+ const struct btf_member *member,
+ void *kdata, const void *udata)
+{
+ const struct fuse_ops *uf_ops;
+ struct fuse_ops *f_ops;
+ u32 moff;
+
+ uf_ops = (const struct fuse_ops *)udata;
+ f_ops = (struct fuse_ops *)kdata;
+
+ moff = __btf_member_bit_offset(t, member) / 8;
+ switch (moff) {
+ case offsetof(struct fuse_ops, name):
+ if (bpf_obj_name_cpy(f_ops->name, uf_ops->name,
+ sizeof(f_ops->name)) <= 0)
+ return -EINVAL;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int bpf_fuse_init(struct btf *btf)
+{
+ s32 type_id;
+
+ type_id = btf_find_by_name_kind(btf, "fuse_buffer", BTF_KIND_STRUCT);
+ if (type_id < 0)
+ return -EINVAL;
+ fuse_buffer_struct_type = btf_type_by_id(btf, type_id);
+
+ return 0;
+}
+
+static struct bpf_fuse_ops_attach *fuse_reg = NULL;
+
+static int bpf_fuse_reg(void *kdata)
+{
+ if (fuse_reg)
+ return fuse_reg->fuse_register_bpf(kdata);
+ pr_warn("Cannot register fuse_ops, FUSE not found");
+ return -EOPNOTSUPP;
+}
+
+static void bpf_fuse_unreg(void *kdata)
+{
+ if(fuse_reg)
+ return fuse_reg->fuse_unregister_bpf(kdata);
+}
+
+int register_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops)
+{
+ fuse_reg = reg_ops;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(register_fuse_bpf);
+
+void unregister_fuse_bpf(struct bpf_fuse_ops_attach *reg_ops)
+{
+ if (reg_ops == fuse_reg)
+ fuse_reg = NULL;
+ else
+ pr_warn("Refusing to unregister unregistered FUSE");
+}
+EXPORT_SYMBOL_GPL(unregister_fuse_bpf);
+
+/* CFI Stubs */
+static uint32_t bpf_fuse_default_filter(const struct bpf_fuse_meta_info *meta)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_open_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_open_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_open_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_open_in *in,
+ struct fuse_open_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_opendir_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_open_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_opendir_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_open_in *in,
+ struct fuse_open_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_create_open_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_create_in *in, struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_create_open_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_create_in *in, const struct fuse_buffer *name,
+ struct fuse_entry_out *entry_out, struct fuse_open_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_release_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_release_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_release_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_release_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_releasedir_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_release_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_releasedir_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_release_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_flush_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_flush_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_flush_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_flush_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_lseek_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_lseek_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_lseek_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_lseek_in *in,
+ struct fuse_lseek_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_copy_file_range_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_copy_file_range_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_copy_file_range_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_copy_file_range_in *in,
+ struct fuse_write_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_fsync_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fsync_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_fsync_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fsync_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_dir_fsync_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fsync_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_dir_fsync_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fsync_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_getxattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getxattr_in *in, struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_getxattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getxattr_in *in, const struct fuse_buffer *name,
+ struct fuse_buffer *value, struct fuse_getxattr_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_listxattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getxattr_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_listxattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getxattr_in *in,
+ struct fuse_buffer *value, struct fuse_getxattr_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_setxattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_setxattr_in *in, struct fuse_buffer *name,
+ struct fuse_buffer *value)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_setxattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_setxattr_in *in, const struct fuse_buffer *name,
+ const struct fuse_buffer *value)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_removexattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_removexattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_read_iter_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_read_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_read_iter_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_read_in *in,
+ struct fuse_read_iter_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_write_iter_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_write_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_write_iter_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_write_in *in,
+ struct fuse_write_iter_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_file_fallocate_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_fallocate_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_file_fallocate_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_fallocate_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_lookup_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_lookup_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name,
+ struct fuse_entry_out *out, struct fuse_buffer *entries)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_mknod_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_mknod_in *in, struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_mknod_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_mknod_in *in, const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_mkdir_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_mkdir_in *in, struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_mkdir_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_mkdir_in *in, const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rmdir_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rmdir_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rename2_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_rename2_in *in, struct fuse_buffer *old_name,
+ struct fuse_buffer *new_name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rename2_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_rename2_in *in, const struct fuse_buffer *old_name,
+ const struct fuse_buffer *new_name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rename_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_rename_in *in, struct fuse_buffer *old_name,
+ struct fuse_buffer *new_name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_rename_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_rename_in *in, const struct fuse_buffer *old_name,
+ const struct fuse_buffer *new_name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_unlink_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_unlink_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_link_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_link_in *in, struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_link_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_link_in *in, const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_getattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_getattr_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_getattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_getattr_in *in,
+ struct fuse_attr_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_setattr_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_setattr_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_setattr_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_setattr_in *in,
+ struct fuse_attr_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_statfs_prefilter(const struct bpf_fuse_meta_info *meta)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_statfs_postfilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_statfs_out *out)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_get_link_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_get_link_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_symlink_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_buffer *name, struct fuse_buffer *path)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_symlink_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_buffer *name, const struct fuse_buffer *path)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_readdir_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_read_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_readdir_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_read_in *in,
+ struct fuse_read_out *out, struct fuse_buffer *buffer)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_access_prefilter(const struct bpf_fuse_meta_info *meta,
+ struct fuse_access_in *in)
+{
+ return 0;
+}
+
+static uint32_t bpf_fuse_access_postfilter(const struct bpf_fuse_meta_info *meta,
+ const struct fuse_access_in *in)
+{
+ return 0;
+}
+
+static struct fuse_ops __bpf_fuse_ops = {
+ .default_filter = bpf_fuse_default_filter,
+ .open_prefilter = bpf_fuse_open_prefilter,
+ .open_postfilter = bpf_fuse_open_postfilter,
+ .opendir_prefilter = bpf_fuse_opendir_prefilter,
+ .opendir_postfilter = bpf_fuse_opendir_postfilter,
+ .create_open_prefilter = bpf_fuse_create_open_prefilter,
+ .create_open_postfilter = bpf_fuse_create_open_postfilter,
+ .release_prefilter = bpf_fuse_release_prefilter,
+ .release_postfilter = bpf_fuse_release_postfilter,
+ .releasedir_prefilter = bpf_fuse_releasedir_prefilter,
+ .releasedir_postfilter = bpf_fuse_releasedir_postfilter,
+ .flush_prefilter = bpf_fuse_flush_prefilter,
+ .flush_postfilter = bpf_fuse_flush_postfilter,
+ .lseek_prefilter = bpf_fuse_lseek_prefilter,
+ .lseek_postfilter = bpf_fuse_lseek_postfilter,
+ .copy_file_range_prefilter = bpf_fuse_copy_file_range_prefilter,
+ .copy_file_range_postfilter = bpf_fuse_copy_file_range_postfilter,
+ .fsync_prefilter = bpf_fuse_fsync_prefilter,
+ .fsync_postfilter = bpf_fuse_fsync_postfilter,
+ .dir_fsync_prefilter = bpf_fuse_dir_fsync_prefilter,
+ .dir_fsync_postfilter = bpf_fuse_dir_fsync_postfilter,
+ .getxattr_prefilter = bpf_fuse_getxattr_prefilter,
+ .getxattr_postfilter = bpf_fuse_getxattr_postfilter,
+ .listxattr_prefilter = bpf_fuse_listxattr_prefilter,
+ .listxattr_postfilter = bpf_fuse_listxattr_postfilter,
+ .setxattr_prefilter = bpf_fuse_setxattr_prefilter,
+ .setxattr_postfilter = bpf_fuse_setxattr_postfilter,
+ .removexattr_prefilter = bpf_fuse_removexattr_prefilter,
+ .removexattr_postfilter = bpf_fuse_removexattr_postfilter,
+ .read_iter_prefilter = bpf_fuse_read_iter_prefilter,
+ .read_iter_postfilter = bpf_fuse_read_iter_postfilter,
+ .write_iter_prefilter = bpf_fuse_write_iter_prefilter,
+ .write_iter_postfilter = bpf_fuse_write_iter_postfilter,
+ .file_fallocate_prefilter = bpf_fuse_file_fallocate_prefilter,
+ .file_fallocate_postfilter = bpf_fuse_file_fallocate_postfilter,
+ .lookup_prefilter = bpf_fuse_lookup_prefilter,
+ .lookup_postfilter = bpf_fuse_lookup_postfilter,
+ .mknod_prefilter = bpf_fuse_mknod_prefilter,
+ .mknod_postfilter = bpf_fuse_mknod_postfilter,
+ .mkdir_prefilter = bpf_fuse_mkdir_prefilter,
+ .mkdir_postfilter = bpf_fuse_mkdir_postfilter,
+ .rmdir_prefilter = bpf_fuse_rmdir_prefilter,
+ .rmdir_postfilter = bpf_fuse_rmdir_postfilter,
+ .rename2_prefilter = bpf_fuse_rename2_prefilter,
+ .rename2_postfilter = bpf_fuse_rename2_postfilter,
+ .rename_prefilter = bpf_fuse_rename_prefilter,
+ .rename_postfilter = bpf_fuse_rename_postfilter,
+ .unlink_prefilter = bpf_fuse_unlink_prefilter,
+ .unlink_postfilter = bpf_fuse_unlink_postfilter,
+ .link_prefilter = bpf_fuse_link_prefilter,
+ .link_postfilter = bpf_fuse_link_postfilter,
+ .getattr_prefilter = bpf_fuse_getattr_prefilter,
+ .getattr_postfilter = bpf_fuse_getattr_postfilter,
+ .setattr_prefilter = bpf_fuse_setattr_prefilter,
+ .setattr_postfilter = bpf_fuse_setattr_postfilter,
+ .statfs_prefilter = bpf_fuse_statfs_prefilter,
+ .statfs_postfilter = bpf_fuse_statfs_postfilter,
+ .get_link_prefilter = bpf_fuse_get_link_prefilter,
+ .get_link_postfilter = bpf_fuse_get_link_postfilter,
+ .symlink_prefilter = bpf_fuse_symlink_prefilter,
+ .symlink_postfilter = bpf_fuse_symlink_postfilter,
+ .readdir_prefilter = bpf_fuse_readdir_prefilter,
+ .readdir_postfilter = bpf_fuse_readdir_postfilter,
+ .access_prefilter = bpf_fuse_access_prefilter,
+ .access_postfilter = bpf_fuse_access_postfilter,
+};
+
+static struct bpf_struct_ops bpf_fuse_ops = {
+ .verifier_ops = &bpf_fuse_verifier_ops,
+ .reg = bpf_fuse_reg,
+ .unreg = bpf_fuse_unreg,
+ .check_member = bpf_fuse_check_member,
+ .init_member = bpf_fuse_init_member,
+ .init = bpf_fuse_init,
+ .name = "fuse_ops",
+ .cfi_stubs = &__bpf_fuse_ops,
+ .owner = THIS_MODULE,
+};
+
+static int __init bpf_fuse_ops_init(void)
+{
+ int ret;
+
+ ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
+ &bpf_fuse_kfunc_set);
+ ret = ret ?: register_bpf_struct_ops(&bpf_fuse_ops, fuse_ops);
+
+ return ret;
+}
+late_initcall(bpf_fuse_ops_init);
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 170d017e8e4a..29f7db457127 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -26,6 +26,7 @@
#include <linux/bsearch.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
+#include <linux/bpf_fuse.h>

#include <net/netfilter/nf_bpf_link.h>

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index ee86e4d7d5fc..eb0808ed3cd9 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -10947,6 +10947,8 @@ enum special_kfunc_type {
KF_bpf_percpu_obj_drop_impl,
KF_bpf_throw,
KF_bpf_iter_css_task_new,
+ KF_bpf_fuse_get_rw_dynptr,
+ KF_bpf_fuse_get_ro_dynptr,
};

BTF_SET_START(special_kfunc_set)
@@ -10973,6 +10975,8 @@ BTF_ID(func, bpf_throw)
#ifdef CONFIG_CGROUPS
BTF_ID(func, bpf_iter_css_task_new)
#endif
+BTF_ID(func, bpf_fuse_get_rw_dynptr)
+BTF_ID(func, bpf_fuse_get_ro_dynptr)
BTF_SET_END(special_kfunc_set)

BTF_ID_LIST(special_kfunc_list)
@@ -11003,6 +11007,8 @@ BTF_ID(func, bpf_iter_css_task_new)
#else
BTF_ID_UNUSED
#endif
+BTF_ID(func, bpf_fuse_get_rw_dynptr)
+BTF_ID(func, bpf_fuse_get_ro_dynptr)

static bool is_kfunc_ret_null(struct bpf_kfunc_call_arg_meta *meta)
{
@@ -11793,7 +11799,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
verbose(env, "verifier internal error: missing ref obj id for parent of clone\n");
return -EFAULT;
}
- }
+ } else if (meta->func_id == special_kfunc_list[KF_bpf_fuse_get_rw_dynptr] ||
+ meta->func_id == special_kfunc_list[KF_bpf_fuse_get_ro_dynptr])
+ dynptr_arg_type |= DYNPTR_TYPE_LOCAL;

ret = process_dynptr_func(env, regno, insn_idx, dynptr_arg_type, clone_ref_obj_id);
if (ret < 0)
--
2.44.0.478.gd926399ef9-goog