summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoanne Koong <joannelkoong@gmail.com>2026-07-16 11:31:43 -0700
committerMiklos Szeredi <mszeredi@redhat.com>2026-07-17 13:06:35 +0200
commit4ef7c8cc9894fccc7aa5fdaf6b39faa45c58c23e (patch)
tree26c040d624ec6f51025990c3d64b42de34ee3d0d
parentedb310bc27f0ad83e7fd558a3caf1a94ca511654 (diff)
downloadlinux-4ef7c8cc9894fccc7aa5fdaf6b39faa45c58c23e.tar.gz
linux-4ef7c8cc9894fccc7aa5fdaf6b39faa45c58c23e.zip
fuse: use release/acquire for fch->initialized
fuse_chan_set_initialized() sets values for the connection state and then sets fch->initialized to true, but lockless readers read fch->initialized and if true, go to read the connection state values, without using any barriers. There are a few instances where this happens (fuse_uring_cmd() before dispatching register / commit-and-fetch cmds, fuse_dev_do_wriite() for handling notify retrieves, etc). To make this as simple as possible, use release/acquire semantics for writing/reading fch->initialized. Add the missing read barriers. This is not marked for stable as these are not realistically reachable on a well-behaved server, and buggy/malicious servers who trigger this path fail benignly rather than crash or deadlock the kernel. Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/cuse.c3
-rw-r--r--fs/fuse/dev.c14
-rw-r--r--fs/fuse/dev_uring.c4
3 files changed, 11 insertions, 10 deletions
diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c
index 3c15b5ba16d7..96d57735a79f 100644
--- a/fs/fuse/cuse.c
+++ b/fs/fuse/cuse.c
@@ -530,7 +530,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&cc->list);
- cc->fc.chan->initialized = 1;
+ /* Pairs with smp_load_acquire() readers of fch->initialized */
+ smp_store_release(&cc->fc.chan->initialized, 1);
rc = cuse_send_init(cc);
if (rc) {
fuse_dev_put(fud);
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index a27ea64d763a..27dafda2a841 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -77,20 +77,17 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa
fch->max_pages = param->max_pages;
}
- /* Make sure stores before this are seen on another CPU */
- smp_wmb();
- fch->initialized = 1;
+ /* Pairs with smp_load_acquire() readers of fch->initialized */
+ smp_store_release(&fch->initialized, 1);
wake_up_all(&fch->blocked_waitq);
}
static bool fuse_block_alloc(struct fuse_chan *fch, bool for_background)
{
- if (!fch->initialized)
+ /* Pairs with smp_store_release() in fuse_chan_set_initialized() */
+ if (!smp_load_acquire(&fch->initialized))
return true;
- /* Pairs with smp_wmb() in fuse_chan_set_initialized() */
- smp_rmb();
-
return (for_background && fch->blocked) ||
(fch->io_uring && fch->connected && !fuse_uring_ready(fch));
}
@@ -1892,7 +1889,8 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
* initialized and connected state
*/
err = -EINVAL;
- if (!fch->initialized || !fch->connected)
+ /* Pairs with smp_store_release() in fuse_chan_set_initialized() */
+ if (!smp_load_acquire(&fch->initialized) || !fch->connected)
goto copy_finish;
/* Don't try to move folios (yet) */
diff --git a/fs/fuse/dev_uring.c b/fs/fuse/dev_uring.c
index 77c8cec43d9c..51f985154aa1 100644
--- a/fs/fuse/dev_uring.c
+++ b/fs/fuse/dev_uring.c
@@ -1251,8 +1251,10 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
/*
* fuse_uring_register() needs the ring to be initialized,
* we need to know the max payload size
+ *
+ * Pairs with smp_store_release() in fuse_chan_set_initialized()
*/
- if (!fch->initialized)
+ if (!smp_load_acquire(&fch->initialized))
return -EAGAIN;
switch (cmd_op) {