lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20230418014037.2412394-29-drosen@google.com>
Date:   Mon, 17 Apr 2023 18:40:28 -0700
From:   Daniel Rosenberg <drosen@...gle.com>
To:     Miklos Szeredi <miklos@...redi.hu>, bpf@...r.kernel.org,
        Alexei Starovoitov <ast@...nel.org>
Cc:     Amir Goldstein <amir73il@...il.com>, linux-kernel@...r.kernel.org,
        linux-fsdevel@...r.kernel.org, linux-unionfs@...r.kernel.org,
        Daniel Borkmann <daniel@...earbox.net>,
        John Fastabend <john.fastabend@...il.com>,
        Andrii Nakryiko <andrii@...nel.org>,
        Martin KaFai Lau <martin.lau@...ux.dev>,
        Song Liu <song@...nel.org>, Yonghong Song <yhs@...com>,
        KP Singh <kpsingh@...nel.org>,
        Stanislav Fomichev <sdf@...gle.com>,
        Hao Luo <haoluo@...gle.com>, Jiri Olsa <jolsa@...nel.org>,
        Shuah Khan <shuah@...nel.org>,
        Jonathan Corbet <corbet@....net>,
        Joanne Koong <joannelkoong@...il.com>,
        Mykola Lysenko <mykolal@...com>, kernel-team@...roid.com,
        Daniel Rosenberg <drosen@...gle.com>
Subject: [RFC PATCH v3 28/37] WIP: bpf: Add fuse_ops struct_op programs

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@...gle.com>
---
 include/linux/bpf_fuse.h          | 189 +++++++++++++++++++++++
 kernel/bpf/Makefile               |   4 +
 kernel/bpf/bpf_fuse.c             | 241 ++++++++++++++++++++++++++++++
 kernel/bpf/bpf_struct_ops_types.h |   4 +
 kernel/bpf/btf.c                  |   1 +
 kernel/bpf/verifier.c             |   9 ++
 6 files changed, 448 insertions(+)
 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 1d3892168d32..26a2e741ef61 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -45,3 +45,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..35125c1f8eef
--- /dev/null
+++ b/kernel/bpf/bpf_fuse.c
@@ -0,0 +1,241 @@
+// 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);
+
+__diag_push();
+__diag_ignore_all("-Wmissing-prototypes",
+                  "Global kfuncs as their definitions will be in BTF");
+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);
+}
+
+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);
+}
+
+uint32_t bpf_fuse_return_len(struct fuse_buffer *buffer)
+{
+	return buffer->size;
+}
+__diag_pop();
+BTF_SET8_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_SET8_END(fuse_kfunc_set)
+
+static const struct btf_kfunc_id_set bpf_fuse_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &fuse_kfunc_set,
+};
+
+static int __init bpf_fuse_kfuncs_init(void)
+{
+	return register_btf_kfunc_id_set(BPF_PROG_TYPE_STRUCT_OPS,
+					 &bpf_fuse_kfunc_set);
+}
+
+late_initcall(bpf_fuse_kfuncs_init);
+
+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);
+	}
+}
+
+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;
+		//if (tcp_ca_find(utcp_ca->name))
+		//	return -EEXIST;
+		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);
+
+/* "extern" is to avoid sparse warning.  It is only used in bpf_struct_ops.c. */
+extern struct bpf_struct_ops bpf_fuse_ops;
+
+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",
+};
+
diff --git a/kernel/bpf/bpf_struct_ops_types.h b/kernel/bpf/bpf_struct_ops_types.h
index 5678a9ddf817..fabb2c1a9482 100644
--- a/kernel/bpf/bpf_struct_ops_types.h
+++ b/kernel/bpf/bpf_struct_ops_types.h
@@ -5,6 +5,10 @@
 #ifdef CONFIG_NET
 BPF_STRUCT_OPS_TYPE(bpf_dummy_ops)
 #endif
+#ifdef CONFIG_FUSE_BPF
+#include <linux/bpf_fuse.h>
+BPF_STRUCT_OPS_TYPE(fuse_ops)
+#endif
 #ifdef CONFIG_INET
 #include <net/tcp.h>
 BPF_STRUCT_OPS_TYPE(tcp_congestion_ops)
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 027f9f8a3551..c34fd9e70039 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -25,6 +25,7 @@
 #include <linux/bsearch.h>
 #include <linux/kobject.h>
 #include <linux/sysfs.h>
+#include <linux/bpf_fuse.h>
 #include <net/sock.h>
 #include "../tools/lib/bpf/relo_core.h"
 
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index fd959824469d..b3bda15283c0 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9597,6 +9597,8 @@ enum special_kfunc_type {
 	KF_bpf_dynptr_from_xdp,
 	KF_bpf_dynptr_slice,
 	KF_bpf_dynptr_slice_rdwr,
+	KF_bpf_fuse_get_rw_dynptr,
+	KF_bpf_fuse_get_ro_dynptr,
 };
 
 BTF_SET_START(special_kfunc_set)
@@ -9616,6 +9618,8 @@ BTF_ID(func, bpf_dynptr_from_skb)
 BTF_ID(func, bpf_dynptr_from_xdp)
 BTF_ID(func, bpf_dynptr_slice)
 BTF_ID(func, bpf_dynptr_slice_rdwr)
+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)
@@ -9637,6 +9641,8 @@ BTF_ID(func, bpf_dynptr_from_skb)
 BTF_ID(func, bpf_dynptr_from_xdp)
 BTF_ID(func, bpf_dynptr_slice)
 BTF_ID(func, bpf_dynptr_slice_rdwr)
+BTF_ID(func, bpf_fuse_get_rw_dynptr)
+BTF_ID(func, bpf_fuse_get_ro_dynptr)
 
 static bool is_kfunc_bpf_rcu_read_lock(struct bpf_kfunc_call_arg_meta *meta)
 {
@@ -10349,6 +10355,9 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 				dynptr_arg_type |= DYNPTR_TYPE_SKB;
 			else if (meta->func_id == special_kfunc_list[KF_bpf_dynptr_from_xdp])
 				dynptr_arg_type |= DYNPTR_TYPE_XDP;
+			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);
 			if (ret < 0)
-- 
2.40.0.634.g4ca3ef3211-goog

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ