Add check_system_io_uring() to determine if system-wide io_uring is available for a FUSE mount. This is useful because FUSE io_uring can only be enabled if the system allows it. Main issue with fuse-io-uring is that the mount point hangs until queues are initialized. If system wide io-uring is disabled queues cannot be initialized and the mount will hang till forcefully umounted. Libfuse solves that by setting up the ring before replying to FUSE_INIT, but we also have to consider other implementations and might get easily missed in development. When mount specifies user_id and group_id (e.g., via unprivileged fusermount with s-bit) not equal 0, the permission check must use the daemon's credentials, not the mount task's (root) credentials. Otherwise io_uring_allowed() incorrectly allows io_uring due to root's CAP_SYS_ADMIN capability. Signed-off-by: Bernd Schubert --- fs/fuse/fuse_i.h | 3 +++ fs/fuse/inode.c | 45 ++++++++++++++++++++++++++++++++++++++- include/linux/io_uring.h | 1 + include/linux/io_uring/io_uring.h | 7 ++++++ io_uring/io_uring.c | 4 +++- 5 files changed, 58 insertions(+), 2 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index c2f2a48156d6c52c8db87a5c092f51d1627deae9..d566e6d3fd19c0eb0d2ee384b734f3950e2e105a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -907,6 +907,9 @@ struct fuse_conn { /* Is synchronous FUSE_INIT allowed? */ unsigned int sync_init:1; + /* If system IO-uring possible */ + unsigned int system_io_uring:1; + /* Use io_uring for communication */ unsigned int io_uring; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index d1babf56f25470fcc08fe400467b3450e8b7464a..6dcbaec9b369c689bc423da64b95f16e38ac0311 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1519,7 +1520,7 @@ static struct fuse_init_args *fuse_new_init(struct fuse_mount *fm) * This is just an information flag for fuse server. No need to check * the reply - server is either sending IORING_OP_URING_CMD or not. */ - if (fuse_uring_enabled()) + if (fm->fc->system_io_uring && fuse_uring_enabled()) flags |= FUSE_OVER_IO_URING; ia->in.flags = flags; @@ -1935,6 +1936,46 @@ int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx) } EXPORT_SYMBOL_GPL(fuse_fill_super_common); +/* Check if system wide io-uring is enabled */ +static void check_system_io_uring(struct fuse_conn *fc, struct fuse_fs_context *ctx) +{ + struct cred *new_cred = NULL; + const struct cred *old_cred = NULL; + int allowed; + + /* + * Mount might be from an unprivileged user using s-bit + * fusermount, the check if system wide io-uring is enabled + * needs to drop privileges + * then. + */ + if (ctx->user_id.val != 0 && ctx->group_id.val != 0) { + new_cred = prepare_creds(); + if (!new_cred) + return; + + cap_clear(new_cred->cap_effective); + cap_clear(new_cred->cap_permitted); + cap_clear(new_cred->cap_inheritable); + + if (ctx->user_id_present) + new_cred->uid = new_cred->euid = ctx->user_id; + + if (ctx->group_id_present) + new_cred->gid = new_cred->egid = new_cred->fsgid = ctx->group_id; + + old_cred = override_creds(new_cred); + } + + allowed = io_uring_allowed(); + fc->system_io_uring = io_uring_allowed() == 0; + + if (old_cred) + revert_creds(old_cred); + if (new_cred) + put_cred(new_cred); +} + static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc) { struct fuse_fs_context *ctx = fsc->fs_private; @@ -1962,6 +2003,8 @@ static int fuse_fill_super(struct super_block *sb, struct fs_context *fsc) fm = get_fuse_mount_super(sb); + check_system_io_uring(fm->fc, ctx); + return fuse_send_init(fm); } diff --git a/include/linux/io_uring.h b/include/linux/io_uring.h index 85fe4e6b275c7de260ea9a8552b8e1c3e7f7e5ec..eaee221b1ed566fcba5a01885e6a4b9073026f93 100644 --- a/include/linux/io_uring.h +++ b/include/linux/io_uring.h @@ -12,6 +12,7 @@ void __io_uring_free(struct task_struct *tsk); void io_uring_unreg_ringfd(void); const char *io_uring_get_opcode(u8 opcode); bool io_is_uring_fops(struct file *file); +int io_uring_allowed(void); static inline void io_uring_files_cancel(void) { diff --git a/include/linux/io_uring/io_uring.h b/include/linux/io_uring/io_uring.h new file mode 100644 index 0000000000000000000000000000000000000000..a28d58ea218ff7cc7518a66bd37ece1eacee30fb --- /dev/null +++ b/include/linux/io_uring/io_uring.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _LINUX_IO_URING_H +#define _LINUX_IO_URING_H + +int io_uring_allowed(void); + +#endif diff --git a/io_uring/io_uring.c b/io_uring/io_uring.c index 820ef05276667e74c259723bf9f3c605cf9d0505..52cb209d4c7499620ae5d8b7ad1362810e84821f 100644 --- a/io_uring/io_uring.c +++ b/io_uring/io_uring.c @@ -76,6 +76,7 @@ #include #include +#include #include "io-wq.h" @@ -3936,7 +3937,7 @@ static long io_uring_setup(u32 entries, struct io_uring_params __user *params) return io_uring_create(entries, &p, params); } -static inline int io_uring_allowed(void) +int io_uring_allowed(void) { int disabled = READ_ONCE(sysctl_io_uring_disabled); kgid_t io_uring_group; @@ -3957,6 +3958,7 @@ static inline int io_uring_allowed(void) allowed_lsm: return security_uring_allowed(); } +EXPORT_SYMBOL_GPL(io_uring_allowed); SYSCALL_DEFINE2(io_uring_setup, u32, entries, struct io_uring_params __user *, params) --- base-commit: 6548d364a3e850326831799d7e3ea2d7bb97ba08 change-id: 20251021-io-uring-fix-check-systemwide-io-uring-enable-f290e75be229 Best regards, -- Bernd Schubert