When running test_stress_04.sh, the following warning is triggered: WARNING: CPU: 1 PID: 135 at drivers/block/ublk_drv.c:1933 ublk_ch_release+0x423/0x4b0 [ublk_drv] This happens when the daemon is abruptly killed: - some references may still be held, because registering IO buffer doesn't grab ublk char device reference OR - io->task_registered_buffers won't be cleared because io buffer is released from non-daemon context For zero-copy and auto buffer register modes, I/O reference crosses syscalls, so IO reference may not be dropped naturally when ublk server is killed abruptly. However, when releasing io_uring context, it is guaranteed that the reference is dropped finally, see io_sqe_buffers_unregister() from io_ring_ctx_free(). Fix this by adding ublk_drain_io_references() that: - Waits for active I/O references dropped - Reinitializes io->ref and io->task_registered_buffers to clean state This way won't hang because releasing ublk char device doesn't depend on unregistering sqe buffers in do_exit(). This ensures the reference count state is clean when ublk_queue_reinit() is called, preventing the warning and potential use-after-free. Fixes: 1f6540e2aabb ("ublk: zc register/unregister bvec") Fixes: 1ceeedb59749 ("ublk: optimize UBLK_IO_UNREGISTER_IO_BUF on daemon task") Fixes: 8a8fe42d765b ("ublk: optimize UBLK_IO_REGISTER_IO_BUF on daemon task") Signed-off-by: Ming Lei --- drivers/block/ublk_drv.c | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 99abd67b708b..21a8f76e4685 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -1595,6 +1595,48 @@ static void ublk_set_canceling(struct ublk_device *ub, bool canceling) ublk_get_queue(ub, i)->canceling = canceling; } +static bool has_io_with_active_ref(struct ublk_queue *ubq) +{ + int i; + + for (i = 0; i < ubq->q_depth; i++) { + struct ublk_io *io = &ubq->ios[i]; + unsigned int refs = refcount_read(&io->ref) + + io->task_registered_buffers; + + /* UBLK_REFCOUNT_INIT or zero means no active reference */ + if (refs != UBLK_REFCOUNT_INIT && refs != 0) + return true; + } + return false; +} + +static void ublk_drain_io_references(struct ublk_device *ub) +{ + int i, j; + + for (i = 0; i < ub->dev_info.nr_hw_queues; i++) { + struct ublk_queue *ubq = ublk_get_queue(ub, i); + + if (!ublk_need_req_ref(ubq)) + continue; + + while (has_io_with_active_ref(ubq)) + mdelay(3); + + for (j = 0; j < ubq->q_depth; j++) { + struct ublk_io *io = &ubq->ios[j]; + /* + * Reinitialize reference counting fields after + * draining. This ensures clean state for queue + * reinitialization. + */ + refcount_set(&io->ref, 0); + io->task_registered_buffers = 0; + } + } +} + static int ublk_ch_release(struct inode *inode, struct file *filp) { struct ublk_device *ub = filp->private_data; @@ -1609,6 +1651,20 @@ static int ublk_ch_release(struct inode *inode, struct file *filp) if (!disk) goto out; + /* + * For zero-copy and auto buffer register modes, I/O references + * might not be dropped naturally when the daemon is killed, but + * io_uring guarantees that registered bvec kernel buffers are + * unregistered finally when freeing io_uring context, then the + * active references are dropped. + * + * Wait until active references are dropped for avoiding use-after-free + * + * This way won't hang because releasing ublk char device doesn't + * rely on unregistering sqe buffers in do_exit() path. + */ + ublk_drain_io_references(ub); + /* * All uring_cmd are done now, so abort any request outstanding to * the ublk server -- 2.47.0