Commit 8741d073 authored by Ming Lei's avatar Ming Lei Committed by Jens Axboe
Browse files

ublk: make sure ubq->canceling is set when queue is frozen



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: 216c8f5e ("ublk: replace monitor with cancelable uring_cmd")
Signed-off-by: default avatarMing Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250327095123.179113-2-ming.lei@redhat.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 04491732
Loading
Loading
Loading
Loading
+29 −10
Original line number Diff line number Diff line
@@ -1446,18 +1446,28 @@ static void ublk_abort_queue(struct ublk_device *ub, struct ublk_queue *ubq)
	}
}

static bool ublk_abort_requests(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)
{
	struct gendisk *disk;
	bool canceled;

	spin_lock(&ubq->cancel_lock);
	if (ubq->canceling) {
		spin_unlock(&ubq->cancel_lock);
		return false;
	}
	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;

	if (was_canceled)
		return false;

	spin_lock(&ub->lock);
	disk = ub->ub_disk;
	if (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);
	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,