[PATCH] ublk_drv: fix NULL pointer dereference in ublk_ctrl_start_recovery()

From: linan666
Date: Tue May 28 2024 - 22:02:04 EST


From: Li Nan <linan122@xxxxxxxxxx>

When two UBLK_CMD_START_USER_RECOVERY commands are submitted, the
first one sets 'ubq->ubq_daemon' to NULL, and the second one triggers
WARN in ublk_queue_reinit() and subsequently a NULL pointer dereference
issue.

Continuing execution after WARN is incorrect, as 'ubq->ubq_daemon' is
known to be NULL. Fix it by return directly if the WARN is triggered.

Note that WARN will still be triggered after the fix if anyone tries to
start recovery twice.

BUG: kernel NULL pointer dereference, address: 0000000000000028
RIP: 0010:ublk_ctrl_start_recovery.constprop.0+0x82/0x180
Call Trace:
<TASK>
? __die+0x20/0x70
? page_fault_oops+0x75/0x170
? exc_page_fault+0x64/0x140
? asm_exc_page_fault+0x22/0x30
? ublk_ctrl_start_recovery.constprop.0+0x82/0x180
ublk_ctrl_uring_cmd+0x4f7/0x6c0
? pick_next_task_idle+0x26/0x40
io_uring_cmd+0x9a/0x1b0
io_issue_sqe+0x193/0x3f0
io_wq_submit_work+0x9b/0x390
io_worker_handle_work+0x165/0x360
io_wq_worker+0xcb/0x2f0
? finish_task_switch.isra.0+0x203/0x290
? finish_task_switch.isra.0+0x203/0x290
? __pfx_io_wq_worker+0x10/0x10
ret_from_fork+0x2d/0x50
? __pfx_io_wq_worker+0x10/0x10
ret_from_fork_asm+0x1a/0x30
</TASK>

Fixes: c732a852b419 ("ublk_drv: add START_USER_RECOVERY and END_USER_RECOVERY support")
Reported-and-tested-by: Changhui Zhong <czhong@xxxxxxxxxx>
Closes: https://lore.kernel.org/all/CAGVVp+UvLiS+bhNXV-h2icwX1dyybbYHeQUuH7RYqUvMQf6N3w@xxxxxxxxxxxxxx
Signed-off-by: Li Nan <linan122@xxxxxxxxxx>
---
drivers/block/ublk_drv.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 4e159948c912..99b621b2d40f 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -2630,7 +2630,8 @@ static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq)
{
int i;

- WARN_ON_ONCE(!(ubq->ubq_daemon && ubq_daemon_is_dying(ubq)));
+ if (WARN_ON_ONCE(!(ubq->ubq_daemon && ubq_daemon_is_dying(ubq))))
+ return;

/* All old ioucmds have to be completed */
ubq->nr_io_ready = 0;
--
2.39.2