Potentially-blocking splice_reads are allowed for normal filesystems
like NFS because they're blessed by root.

FUSE is commonly used suid-root, and allows anyone to trivially create
a file that, when spliced from, will just sleep forever with the pipe
lock held.

The only way IPC to the fusing process could be avoided is if
!(ff->open_flags & FOPEN_DIRECT_IO) and the range was already cached
and we weren't past the end. Just refuse it.

virtiofs behaves like a normal filesystem and can only be mounted
by root, it's unaffected by use of a new "trusted" connection flag.
This may be extended to include real FUSE mounts by processes which
aren't suid, to match the semantics for normal filesystems.

Signed-off-by: Ahelenia Ziemiańska <nabijaczlew...@nabijaczleweli.xyz>
---
 fs/fuse/file.c      | 17 ++++++++++++++++-
 fs/fuse/fuse_i.h    |  3 +++
 fs/fuse/virtio_fs.c |  1 +
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index a660f1f21540..20bb16ddfcc9 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -3200,6 +3200,21 @@ static ssize_t fuse_copy_file_range(struct file 
*src_file, loff_t src_off,
        return ret;
 }
 
+static long fuse_splice_read(struct file *in, loff_t *ppos,
+                            struct pipe_inode_info *pipe, size_t len,
+                            unsigned int flags)
+{
+       struct inode *inode = file_inode(in);
+
+       if (fuse_is_bad(inode))
+               return -EIO;
+
+       if (get_fuse_conn(inode)->trusted)
+               return filemap_splice_read(in, ppos, pipe, len, flags);
+
+       return -EINVAL;
+}
+
 static const struct file_operations fuse_file_operations = {
        .llseek         = fuse_file_llseek,
        .read_iter      = fuse_file_read_iter,
@@ -3212,7 +3227,7 @@ static const struct file_operations fuse_file_operations 
= {
        .lock           = fuse_file_lock,
        .get_unmapped_area = thp_get_unmapped_area,
        .flock          = fuse_file_flock,
-       .splice_read    = filemap_splice_read,
+       .splice_read    = fuse_splice_read,
        .splice_write   = iter_file_splice_write,
        .unlocked_ioctl = fuse_file_ioctl,
        .compat_ioctl   = fuse_file_compat_ioctl,
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1df83eebda92..463c5d4ad8b4 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -818,6 +818,9 @@ struct fuse_conn {
        /* Is statx not implemented by fs? */
        unsigned int no_statx:1;
 
+       /* Do we trust this connection to always respond? */
+       bool trusted:1;
+
        /** The number of requests waiting for completion */
        atomic_t num_waiting;
 
diff --git a/fs/fuse/virtio_fs.c b/fs/fuse/virtio_fs.c
index 5f1be1da92ce..fce0fe24899a 100644
--- a/fs/fuse/virtio_fs.c
+++ b/fs/fuse/virtio_fs.c
@@ -1448,6 +1448,7 @@ static int virtio_fs_get_tree(struct fs_context *fsc)
        fc->delete_stale = true;
        fc->auto_submounts = true;
        fc->sync_fs = true;
+       fc->trusted = true;
 
        /* Tell FUSE to split requests that exceed the virtqueue's size */
        fc->max_pages_limit = min_t(unsigned int, fc->max_pages_limit,
-- 
2.39.2

Attachment: signature.asc
Description: PGP signature

Reply via email to