]> www.infradead.org Git - nvme.git/commitdiff
ublk: make sure ubq->canceling is set when queue is frozen
authorMing Lei <ming.lei@redhat.com>
Thu, 27 Mar 2025 09:51:10 +0000 (17:51 +0800)
committerKeith Busch <kbusch@kernel.org>
Mon, 31 Mar 2025 15:48:24 +0000 (08:48 -0700)
Now ublk driver depends on `ubq->canceling` for deciding if the request
can be dispatched via uring_cmd & io_uring_cmd_complete_in_task().

Once ubq->canceling is set, the uring_cmd can be done via ublk_cancel_cmd()
and io_uring_cmd_done().

So set ubq->canceling when queue is frozen, this way makes sure that the
flag can be observed from ublk_queue_rq() reliably, and avoids
use-after-free on uring_cmd.

Fixes: 216c8f5ef0f2 ("ublk: replace monitor with cancelable uring_cmd")
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250327095123.179113-2-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/ublk_drv.c

index c060da409ed8a888b7e414c9065efd2cbd6d57d7..fbcb7c2ff8511d352c9f4c54ee559b76dfc375d4 100644 (file)
@@ -1446,17 +1446,27 @@ static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq)
        }
 }
 
+/* Must be called when queue is frozen */
+static bool ublk_mark_queue_canceling(struct ublk_queue *ubq)
+{
+       bool canceled;
+
+       spin_lock(&ubq->cancel_lock);
+       canceled = ubq->canceling;
+       if (!canceled)
+               ubq->canceling = true;
+       spin_unlock(&ubq->cancel_lock);
+
+       return canceled;
+}
+
 static bool ublk_abort_requests(struct ublk_device *ub, struct ublk_queue *ubq)
 {
+       bool was_canceled = ubq->canceling;
        struct gendisk *disk;
 
-       spin_lock(&ubq->cancel_lock);
-       if (ubq->canceling) {
-               spin_unlock(&ubq->cancel_lock);
+       if (was_canceled)
                return false;
-       }
-       ubq->canceling = true;
-       spin_unlock(&ubq->cancel_lock);
 
        spin_lock(&ub->lock);
        disk = ub->ub_disk;
@@ -1468,14 +1478,23 @@ static bool ublk_abort_requests(struct ublk_device *ub, struct ublk_queue *ubq)
        if (!disk)
                return false;
 
-       /* Now we are serialized with ublk_queue_rq() */
+       /*
+        * Now we are serialized with ublk_queue_rq()
+        *
+        * Make sure that ubq->canceling is set when queue is frozen,
+        * because ublk_queue_rq() has to rely on this flag for avoiding to
+        * touch completed uring_cmd
+        */
        blk_mq_quiesce_queue(disk->queue);
-       /* abort queue is for making forward progress */
-       ublk_abort_queue(ub, ubq);
+       was_canceled = ublk_mark_queue_canceling(ubq);
+       if (!was_canceled) {
+               /* abort queue is for making forward progress */
+               ublk_abort_queue(ub, ubq);
+       }
        blk_mq_unquiesce_queue(disk->queue);
        put_device(disk_to_dev(disk));
 
-       return true;
+       return !was_canceled;
 }
 
 static void ublk_cancel_cmd(struct ublk_queue *ubq, struct ublk_io *io,