[PATCH v2] nvmet-fc: flush nvmet_wq twice on targetport unregister

Nguyen Ngoc Thang posted 1 patch 3 days, 3 hours ago
drivers/nvme/target/fc.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH v2] nvmet-fc: flush nvmet_wq twice on targetport unregister
Posted by Nguyen Ngoc Thang 3 days, 3 hours ago
nvmet_fc_delete_assoc_work() sends a Disconnect Association LS. If the
LLDD completes it asynchronously, e.g. fcloop failing it with
-ECONNREFUSED from a work item once the remote port is gone, the
completion is queued from within nvmet_fc_unregister_targetport()'s
flush_workqueue(). flush_workqueue() does not wait for work queued
while it runs, so nvmet_fc_free_pending_reqs() frees the pending lsop
before the completion runs, which then calls lsreq->done() on freed
memory:

  BUG: KASAN: slab-use-after-free in fcloop_rport_lsrqst_work+0x242/0x2e0
  Workqueue: nvmet-wq fcloop_tport_lsrqst_work
  Allocated by nvmet_fc_xmt_disconnect_assoc
  Freed by nvmet_fc_free_pending_reqs
           nvmet_fc_unregister_targetport
           fcloop_delete_target_port

Flush a second time so such completions have run before the remaining
pending requests are freed.

Reported-by: syzbot+77955102efac681ec73b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=77955102efac681ec73b
Fixes: bbccbf791e6f ("nvmet-fc: free pending reqs on tgtport unregister")
Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999@gmail.com>
---
Changes in v2:
- Drop the fcloop change; the async -ECONNREFUSED completion mimics what
  an HBA does (Daniel). Instead flush nvmet_wq a second time in
  nvmet_fc_unregister_targetport() so the LS completion queued by
  nvmet_fc_delete_assoc_work() runs before the pending requests are freed.
- A blktest reproducing this is sent separately (nvme/071) (Hannes).
- v1: https://lore.kernel.org/all/20260920163804.67858-1-ngocthang2710.1999@gmail.com/

 drivers/nvme/target/fc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
index 1b557775e033..cf9fd304a2b5 100644
--- a/drivers/nvme/target/fc.c
+++ b/drivers/nvme/target/fc.c
@@ -1646,6 +1646,8 @@ nvmet_fc_unregister_targetport(struct nvmet_fc_target_port *target_port)
 	/* terminate any outstanding associations */
 	__nvmet_fc_free_assocs(tgtport);
 
+	flush_workqueue(nvmet_wq);
+	/* assoc deletion sends an LS whose completion is queued while flushing */
 	flush_workqueue(nvmet_wq);
 
 	nvmet_fc_free_pending_reqs(tgtport);
-- 
2.43.0