From: Bernd Schubert <bschubert@ddn.com>
To: Miklos Szeredi <miklos@szeredi.hu>, Jens Axboe <axboe@kernel.dk>
Cc: linux-fsdevel@vger.kernel.org, io-uring@vger.kernel.org,
Pavel Begunkov <asml.silence@gmail.com>,
Joanne Koong <joannelkoong@gmail.com>,
Luis Henriques <luis@igalia.com>,
Bernd Schubert <bschubert@ddn.com>
Subject: [PATCH RFC] fuse: check if system-wide io_uring is enabled
Date: Tue, 21 Oct 2025 22:31:25 +0200 [thread overview]
Message-ID: <20251021-io-uring-fix-check-systemwide-io-uring-enable-v1-1-01d4b4a8ef4f@ddn.com> (raw)
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 <bschubert@ddn.com>
---
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 <linux/sched.h>
#include <linux/exportfs.h>
#include <linux/posix_acl.h>
+#include <linux/io_uring/io_uring.h>
#include <linux/pid_namespace.h>
#include <uapi/linux/magic.h>
@@ -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 <trace/events/io_uring.h>
#include <uapi/linux/io_uring.h>
+#include <linux/io_uring/io_uring.h>
#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 <bschubert@ddn.com>
next reply other threads:[~2025-10-21 20:31 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-21 20:31 Bernd Schubert [this message]
2025-10-21 21:56 ` [PATCH RFC] fuse: check if system-wide io_uring is enabled Jens Axboe
2025-10-21 22:08 ` Bernd Schubert
2025-10-21 22:13 ` Jens Axboe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251021-io-uring-fix-check-systemwide-io-uring-enable-v1-1-01d4b4a8ef4f@ddn.com \
--to=bschubert@ddn.com \
--cc=asml.silence@gmail.com \
--cc=axboe@kernel.dk \
--cc=io-uring@vger.kernel.org \
--cc=joannelkoong@gmail.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=luis@igalia.com \
--cc=miklos@szeredi.hu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).