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>
This commit is contained in:
Joanne Koong
2026-07-16 11:31:43 -07:00
committed by Miklos Szeredi
parent edb310bc27
commit 4ef7c8cc98
3 changed files with 11 additions and 10 deletions

View File

@@ -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);

View File

@@ -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) */

View File

@@ -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) {