]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse-uring: Avoid queue->stopped races and set/read that value under lock
authorBernd Schubert <bernd@bsbernd.com>
Mon, 8 Jun 2026 21:03:44 +0000 (23:03 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 15 Jun 2026 12:06:14 +0000 (14:06 +0200)
There are several readers of queue->stopped that check the value
under lock, but fuse_uring_commit_fetch() did not and actually
the value was not set under the lock in fuse_uring_abort_end_requests()
either. Especially in fuse_uring_commit_fetch it is important
to check under a lock, because due to races 'struct fuse_req'
might be freed with fuse_request_end, but another thread/cpu
might already do teardown work.

Cc: stable@kernel.org # 6.14
Fixes: 4a9bfb9b6850fec ("fuse: {io-uring} Handle teardown of ring entries")
Reported-by: Berkant Koc <me@berkoc.com>
Reported-by: xlabai <xlabai@tencent.com>
Signed-off-by: Bernd Schubert <bernd@bsbernd.com>
Reviewed-by: Joanne Koong <joannelkoong@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dev_uring.c

index 38b5a05a423ffd2fb927b2e8ab89ce664d530dee..ed652034927ee231d4c6050e1f7c185c0cd2f04d 100644 (file)
@@ -130,10 +130,9 @@ void fuse_uring_abort_end_requests(struct fuse_ring *ring)
                if (!queue)
                        continue;
 
-               queue->stopped = true;
-
                WARN_ON_ONCE(ring->fc->max_background != UINT_MAX);
                spin_lock(&queue->lock);
+               queue->stopped = true;
                spin_lock(&fc->bg_lock);
                fuse_uring_flush_bg(queue);
                spin_unlock(&fc->bg_lock);
@@ -470,7 +469,7 @@ static void fuse_uring_async_stop_queues(struct work_struct *work)
                                      FUSE_URING_TEARDOWN_INTERVAL);
        } else {
                wake_up_all(&ring->stop_waitq);
-               fuse_conn_put(ring->chan->conn);
+               fuse_conn_put(ring->fc);
        }
 }
 
@@ -482,7 +481,7 @@ void fuse_uring_stop_queues(struct fuse_ring *ring)
        fuse_uring_teardown_all_queues(ring);
 
        if (atomic_read(&ring->queue_refs) > 0) {
-               fuse_conn_get(ring->chan->conn);
+               fuse_conn_get(ring->fc);
                ring->teardown_time = jiffies;
                INIT_DELAYED_WORK(&ring->async_teardown_work,
                                  fuse_uring_async_stop_queues);
@@ -903,10 +902,15 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags,
                return err;
        fpq = &queue->fpq;
 
-       if (!READ_ONCE(fc->connected) || READ_ONCE(queue->stopped))
+       if (!READ_ONCE(fc->connected))
                return err;
 
        spin_lock(&queue->lock);
+       if (unlikely(queue->stopped)) {
+               spin_unlock(&queue->lock);
+               return err;
+       }
+
        /* Find a request based on the unique ID of the fuse request
         * This should get revised, as it needs a hash calculation and list
         * search. And full struct fuse_pqueue is needed (memory overhead).