summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-09-07 12:27:20 +0100
committerMark Brown <broonie@kernel.org>2026-09-07 12:27:20 +0100
commitd341cdc7d12993b75ad37521358a497fd9738c66 (patch)
treea321f8a37bc6b6c140e54ae55f2e5e44f4e46924
parent3c7d33cde0a946f0ae16d3a451cba727327b86b9 (diff)
parent10bd6b32965265688fa277d166ab224de8e86eb2 (diff)
downloadlinux-next-d341cdc7d12993b75ad37521358a497fd9738c66.tar.gz
linux-next-d341cdc7d12993b75ad37521358a497fd9738c66.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse.git
-rw-r--r--fs/fuse/dax.c1
-rw-r--r--fs/fuse/file.c7
-rw-r--r--fs/fuse/fuse_i.h9
-rw-r--r--fs/fuse/inode.c28
-rw-r--r--fs/fuse/ioctl.c3
-rw-r--r--fs/fuse/req.c7
-rw-r--r--include/uapi/linux/fuse.h12
-rw-r--r--tools/testing/selftests/filesystems/fuse/.gitignore1
-rw-r--r--tools/testing/selftests/filesystems/fuse/Makefile2
9 files changed, 59 insertions, 11 deletions
diff --git a/fs/fuse/dax.c b/fs/fuse/dax.c
index 85cdf0199bc0..32c88ef81434 100644
--- a/fs/fuse/dax.c
+++ b/fs/fuse/dax.c
@@ -827,6 +827,7 @@ int fuse_dax_mmap(struct file *file, struct vm_area_struct *vma)
file_accessed(file);
vma->vm_ops = &fuse_dax_vm_ops;
vm_flags_set(vma, VM_MIXEDMAP | VM_HUGEPAGE);
+ vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
return 0;
}
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 9a36d0329e22..f12db1d9556a 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -689,7 +689,7 @@ static void fuse_aio_complete(struct fuse_io_priv *io, int err, ssize_t pos)
struct address_space *mapping = io->iocb->ki_filp->f_mapping;
ssize_t res = fuse_get_res_by_io(io);
- if (res >= 0) {
+ if (res >= 0 && io->write) {
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
@@ -1875,7 +1875,10 @@ static ssize_t fuse_splice_read(struct file *in, loff_t *ppos,
struct fuse_file *ff = in->private_data;
/* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */
- if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO))
+
+ if (ff->open_flags & FOPEN_DIRECT_IO)
+ return copy_splice_read(in, ppos, pipe, len, flags);
+ else if (fuse_file_passthrough(ff))
return fuse_passthrough_splice_read(in, ppos, pipe, len, flags);
else
return filemap_splice_read(in, ppos, pipe, len, flags);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index c8d4c5f3af7e..97d356588d00 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -391,6 +391,7 @@ struct fuse_fs_context {
bool no_control:1;
bool no_force_umount:1;
bool legacy_opts_show:1;
+ bool syncfs_capable:1;
enum fuse_dax_mode dax_mode;
unsigned int max_read;
unsigned int blksize;
@@ -675,6 +676,14 @@ struct fuse_conn {
/** @sync_fs: Propagate syncfs() to server */
unsigned int sync_fs:1;
+ /**
+ * @syncfs_capable: the privilege required to honor FUSE_HAS_SYNCFS was
+ * present when /dev/fuse was opened (CAP_SYS_ADMIN in the initial user
+ * namespace), i.e. the same privilege that mounting virtiofs/fuseblk
+ * requires.
+ */
+ unsigned int syncfs_capable:1;
+
/** @init_security: Initialize security xattrs when creating a new inode */
unsigned int init_security:1;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e9552be3637b..c93d744b661e 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -803,6 +803,15 @@ static int fuse_opt_fd(struct fs_context *fsc, struct file *file)
if (file->f_cred->user_ns != fsc->user_ns)
return invalfc(fsc, "wrong user namespace for fuse device");
+ /*
+ * Record whether the server opened /dev/fuse with CAP_SYS_ADMIN in the
+ * initial user namespace -- the same privilege that mounting virtiofs
+ * or fuseblk requires. Only such servers are trusted to receive
+ * FUSE_SYNCFS (see fuse_syncfs_enable()).
+ */
+ ctx->syncfs_capable = file_ns_capable(file, &init_user_ns,
+ CAP_SYS_ADMIN);
+
ctx->fud = fuse_dev_grab(file);
return 0;
@@ -1269,6 +1278,16 @@ struct fuse_init_args {
struct fuse_mount *fm;
};
+/*
+ * A server can stall syncfs()/sync(), so only honor FUSE_HAS_SYNCFS for
+ * servers that opened /dev/fuse with CAP_SYS_ADMIN in the initial user
+ * namespace -- the same privilege required to mount virtiofs or fuseblk.
+ */
+static bool fuse_syncfs_enable(struct fuse_conn *fc, u64 flags)
+{
+ return (flags & FUSE_HAS_SYNCFS) && fc->syncfs_capable;
+}
+
static void process_init_reply(struct fuse_args *args, int error)
{
struct fuse_init_args *ia = container_of(args, typeof(*ia), args);
@@ -1410,6 +1429,9 @@ static void process_init_reply(struct fuse_args *args, int error)
if (flags & FUSE_REQUEST_TIMEOUT)
timeout = arg->request_timeout;
+
+ if (fuse_syncfs_enable(fc, flags))
+ fc->sync_fs = 1;
} else {
ra_pages = fc->max_read / PAGE_SIZE;
fc->no_lock = 1;
@@ -1479,6 +1501,11 @@ static struct fuse_init_args *fuse_new_init(struct fuse_mount *fm)
flags |= FUSE_SUBMOUNTS;
if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
flags |= FUSE_PASSTHROUGH;
+ /* Only offered to sufficiently privileged servers; see
+ * fuse_syncfs_enable().
+ */
+ if (fm->fc->syncfs_capable)
+ flags |= FUSE_HAS_SYNCFS;
if (fuse_uring_enabled())
flags |= FUSE_OVER_IO_URING | FUSE_HAS_IO_URING_BUFPOOL;
@@ -1770,6 +1797,7 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
fc->default_permissions = ctx->default_permissions;
fc->allow_other = ctx->allow_other;
+ fc->syncfs_capable = ctx->syncfs_capable;
fc->user_id = ctx->user_id;
fc->group_id = ctx->group_id;
fc->legacy_opts_show = ctx->legacy_opts_show;
diff --git a/fs/fuse/ioctl.c b/fs/fuse/ioctl.c
index 3614ea603913..dc3a188f5d72 100644
--- a/fs/fuse/ioctl.c
+++ b/fs/fuse/ioctl.c
@@ -130,9 +130,6 @@ static int fuse_setup_measure_verity(unsigned long arg, struct iovec *iov)
if (copy_from_user(&digest_size, &uarg->digest_size, sizeof(digest_size)))
return -EFAULT;
- if (digest_size > SIZE_MAX - sizeof(struct fsverity_digest))
- return -EINVAL;
-
iov->iov_len = sizeof(struct fsverity_digest) + digest_size;
return 0;
diff --git a/fs/fuse/req.c b/fs/fuse/req.c
index a01ee743d31e..51cfd64ea1ba 100644
--- a/fs/fuse/req.c
+++ b/fs/fuse/req.c
@@ -10,12 +10,12 @@ static int fuse_fill_creds(struct fuse_mount *fm, struct fuse_args *args, struct
kuid_t fsuid = mapped_fsuid(idmap, fc->user_ns);
kgid_t fsgid = mapped_fsgid(idmap, fc->user_ns);
+ if (args->nocreds)
+ return 0;
+
args->pid = pid_nr_ns(task_pid(current), fc->pid_ns);
if (args->force) {
- if (args->nocreds)
- return 0;
-
if (no_idmap) {
args->uid = from_kuid_munged(fc->user_ns, current_fsuid());
args->gid = from_kgid_munged(fc->user_ns, current_fsgid());
@@ -26,7 +26,6 @@ static int fuse_fill_creds(struct fuse_mount *fm, struct fuse_args *args, struct
return 0;
}
- WARN_ON(args->nocreds);
/*
* Keep the old behavior when idmappings support was not
* declared by a FUSE server.
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 7435e09c87fe..10a7f31c4bdf 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -248,6 +248,9 @@
* - add bufpool offset field to fuse_uring_ent_in_out struct
* - add FUSE_URING_ZERO_COPY, FUSE_URING_ENT_ZERO_COPY, and
* FOPEN_IO_URING_ZERO_COPY flag
+ *
+ * 7.47
+ * - add FUSE_HAS_SYNCFS opt-in flag for privileged userspace servers
*/
#ifndef _LINUX_FUSE_H
@@ -283,7 +286,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 46
+#define FUSE_KERNEL_MINOR_VERSION 47
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -464,6 +467,12 @@ struct fuse_file_lock {
* FUSE_REQUEST_TIMEOUT: kernel supports timing out requests.
* init_out.request_timeout contains the timeout (in secs)
* FUSE_HAS_IO_URING_BUFPOOL: kernel supports io-uring buffer pools
+ * FUSE_HAS_SYNCFS: server requests that syncfs()/sync() be propagated as
+ * FUSE_SYNCFS requests. Since an untrusted server can use this
+ * to stall sync(), it is only honored when /dev/fuse was opened
+ * with CAP_SYS_ADMIN in the initial user namespace (the same
+ * privilege that mounting virtiofs or fuseblk requires).
+ * Insufficiently privileged servers ignore it.
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -512,6 +521,7 @@ struct fuse_file_lock {
#define FUSE_OVER_IO_URING (1ULL << 41)
#define FUSE_REQUEST_TIMEOUT (1ULL << 42)
#define FUSE_HAS_IO_URING_BUFPOOL (1ULL << 43)
+#define FUSE_HAS_SYNCFS (1ULL << 44)
/**
* CUSE INIT request/reply flags
diff --git a/tools/testing/selftests/filesystems/fuse/.gitignore b/tools/testing/selftests/filesystems/fuse/.gitignore
index fb51603fe419..b5b03db1118c 100644
--- a/tools/testing/selftests/filesystems/fuse/.gitignore
+++ b/tools/testing/selftests/filesystems/fuse/.gitignore
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
fuse_mnt
fusectl_test
+test_syncfs
write_extend_eof_test
diff --git a/tools/testing/selftests/filesystems/fuse/Makefile b/tools/testing/selftests/filesystems/fuse/Makefile
index 95a1ee947ca7..c2de8d225447 100644
--- a/tools/testing/selftests/filesystems/fuse/Makefile
+++ b/tools/testing/selftests/filesystems/fuse/Makefile
@@ -2,7 +2,7 @@
CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES)
-TEST_GEN_PROGS := fusectl_test
+TEST_GEN_PROGS := fusectl_test test_syncfs
TEST_GEN_PROGS += write_extend_eof_test
TEST_GEN_FILES := fuse_mnt