diff options
| author | Mark Brown <broonie@kernel.org> | 2026-09-07 12:27:20 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-09-07 12:27:20 +0100 |
| commit | d341cdc7d12993b75ad37521358a497fd9738c66 (patch) | |
| tree | a321f8a37bc6b6c140e54ae55f2e5e44f4e46924 | |
| parent | 3c7d33cde0a946f0ae16d3a451cba727327b86b9 (diff) | |
| parent | 10bd6b32965265688fa277d166ab224de8e86eb2 (diff) | |
| download | linux-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.c | 1 | ||||
| -rw-r--r-- | fs/fuse/file.c | 7 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 9 | ||||
| -rw-r--r-- | fs/fuse/inode.c | 28 | ||||
| -rw-r--r-- | fs/fuse/ioctl.c | 3 | ||||
| -rw-r--r-- | fs/fuse/req.c | 7 | ||||
| -rw-r--r-- | include/uapi/linux/fuse.h | 12 | ||||
| -rw-r--r-- | tools/testing/selftests/filesystems/fuse/.gitignore | 1 | ||||
| -rw-r--r-- | tools/testing/selftests/filesystems/fuse/Makefile | 2 |
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 |
