[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20240329015351.624249-6-drosen@google.com>
Date: Thu, 28 Mar 2024 18:53:20 -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>,
Eduard Zingerman <eddyz87@...il.com>, Yonghong Song <yonghong.song@...ux.dev>,
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>,
Christian Brauner <brauner@...nel.org>, kernel-team@...roid.com,
Daniel Rosenberg <drosen@...gle.com>
Subject: [RFC PATCH v4 05/36] fuse-bpf: Add ioctl interface for /dev/fuse
This introduces an alternative method of responding to fuse requests.
Lookups supplying a backing fd or bpf will need to call through the
ioctl to ensure there can be no attempts to fool priveledged processes
into inadvertantly performing other actions.
Signed-off-by: Daniel Rosenberg <drosen@...gle.com>
---
fs/fuse/dev.c | 56 ++++++++++++++++++++++++++++++++-------
fs/fuse/fuse_i.h | 1 +
include/uapi/linux/fuse.h | 1 +
3 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index b413e0bfd61c..e51ec198af3f 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -1016,18 +1016,19 @@ static int fuse_copy_one(struct fuse_copy_state *cs, void *val, unsigned size)
/* Copy the fuse-bpf lookup args and verify them */
#ifdef CONFIG_FUSE_BPF
-static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size)
+static int fuse_copy_lookup(struct fuse_copy_state *cs, unsigned via_ioctl, void *val, unsigned size)
{
struct fuse_bpf_entry_out *fbeo = (struct fuse_bpf_entry_out *)val;
struct fuse_bpf_entry *feb = container_of(fbeo, struct fuse_bpf_entry, out[0]);
int num_entries = size / sizeof(*fbeo);
int err;
- if (size && size % sizeof(*fbeo) != 0)
+ if (size && (size % sizeof(*fbeo) != 0 || !via_ioctl))
return -EINVAL;
if (num_entries > FUSE_BPF_MAX_ENTRIES)
return -EINVAL;
+
err = fuse_copy_one(cs, val, size);
if (err)
return err;
@@ -1036,7 +1037,7 @@ static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size
return err;
}
#else
-static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size)
+static int fuse_copy_lookup(struct fuse_copy_state *cs, unsigned via_ioctl, void *val, unsigned size)
{
return fuse_copy_one(cs, val, size);
}
@@ -1045,7 +1046,7 @@ static int fuse_copy_lookup(struct fuse_copy_state *cs, void *val, unsigned size
/* Copy request arguments to/from userspace buffer */
static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
unsigned argpages, struct fuse_arg *args,
- int zeroing, unsigned is_lookup)
+ int zeroing, unsigned is_lookup, unsigned via_ioct)
{
int err = 0;
unsigned i;
@@ -1055,7 +1056,7 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
if (i == numargs - 1 && argpages)
err = fuse_copy_pages(cs, arg->size, zeroing);
else if (i == numargs - 1 && is_lookup)
- err = fuse_copy_lookup(cs, arg->value, arg->size);
+ err = fuse_copy_lookup(cs, via_ioct, arg->value, arg->size);
else
err = fuse_copy_one(cs, arg->value, arg->size);
}
@@ -1333,7 +1334,7 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
err = fuse_copy_one(cs, &req->in.h, sizeof(req->in.h));
if (!err)
err = fuse_copy_args(cs, args->in_numargs, args->in_pages,
- (struct fuse_arg *) args->in_args, 0, 0);
+ (struct fuse_arg *) args->in_args, 0, 0, 0);
fuse_copy_finish(cs);
spin_lock(&fpq->lock);
clear_bit(FR_LOCKED, &req->flags);
@@ -1872,7 +1873,8 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
lastarg->size -= diffsize;
}
return fuse_copy_args(cs, args->out_numargs, args->out_pages,
- args->out_args, args->page_zeroing, args->is_lookup);
+ args->out_args, args->page_zeroing, args->is_lookup,
+ args->via_ioctl);
}
/*
@@ -1882,7 +1884,7 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_args *args,
* it from the list and copy the rest of the buffer to the request.
* The request is finished by calling fuse_request_end().
*/
-static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
+static ssize_t fuse_dev_do_write(struct fuse_dev *fud, bool from_ioctl,
struct fuse_copy_state *cs, size_t nbytes)
{
int err;
@@ -1954,6 +1956,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
if (!req->args->page_replace)
cs->move_pages = 0;
+ req->args->via_ioctl = from_ioctl;
if (oh.error)
err = nbytes != sizeof(oh) ? -EINVAL : 0;
else
@@ -1992,7 +1995,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
fuse_copy_init(&cs, 0, from);
- return fuse_dev_do_write(fud, &cs, iov_iter_count(from));
+ return fuse_dev_do_write(fud, false, &cs, iov_iter_count(from));
}
static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
@@ -2073,7 +2076,7 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
if (flags & SPLICE_F_MOVE)
cs.move_pages = 1;
- ret = fuse_dev_do_write(fud, &cs, len);
+ ret = fuse_dev_do_write(fud, false, &cs, len);
pipe_lock(pipe);
out_free:
@@ -2286,6 +2289,33 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
return 0;
}
+// Provides an alternate means to respond to a fuse request
+static int fuse_handle_ioc_response(struct fuse_dev *dev, void *buff, uint32_t size)
+{
+ struct fuse_copy_state cs;
+ struct iovec *iov = NULL;
+ struct iov_iter iter;
+ int res;
+
+ if (size > PAGE_SIZE)
+ return -EINVAL;
+ iov = (struct iovec *) __get_free_page(GFP_KERNEL);
+ if (!iov)
+ return -ENOMEM;
+
+ iov->iov_base = buff;
+ iov->iov_len = size;
+
+ iov_iter_init(&iter, READ, iov, 1, size);
+ fuse_copy_init(&cs, 0, &iter);
+
+
+ res = fuse_dev_do_write(dev, true, &cs, size);
+ free_page((unsigned long) iov);
+
+ return res;
+}
+
static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
@@ -2319,6 +2349,12 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
fdput(f);
break;
default:
+ if (_IOC_TYPE(cmd) == FUSE_DEV_IOC_MAGIC
+ && _IOC_NR(cmd) == _IOC_NR(FUSE_DEV_IOC_BPF_RESPONSE(0))
+ && _IOC_DIR(cmd) == _IOC_WRITE) {
+ res = fuse_handle_ioc_response(fuse_get_dev(file), (void *) arg, _IOC_SIZE(cmd));
+ break;
+ }
res = -ENOTTY;
break;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index ac61f08fd85d..dd62e78e474c 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -336,6 +336,7 @@ struct fuse_args {
bool may_block:1;
bool is_ext:1;
bool is_lookup:1;
+ bool via_ioctl:1;
struct fuse_in_arg in_args[3];
struct fuse_arg out_args[2];
void (*end)(struct fuse_mount *fm, struct fuse_args *args, int error);
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 0c0a9a8b5c26..74bc15e1d0b7 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -1075,6 +1075,7 @@ struct fuse_notify_retrieve_in {
/* Device ioctls: */
#define FUSE_DEV_IOC_MAGIC 229
#define FUSE_DEV_IOC_CLONE _IOR(FUSE_DEV_IOC_MAGIC, 0, uint32_t)
+#define FUSE_DEV_IOC_BPF_RESPONSE(N) _IOW(FUSE_DEV_IOC_MAGIC, 125, char[N])
struct fuse_lseek_in {
uint64_t fh;
--
2.44.0.478.gd926399ef9-goog
Powered by blists - more mailing lists