ibmvfc_remove() called ibmvfc_release_sub_crqs() before
ibmvfc_nvme_unregister().  ibmvfc_release_sub_crqs() frees the
nvme_scrqs.scrqs array and sets active_queues to 0, but the nvme-fc
transport is still live at that point and can call back into
ibmvfc_nvme_create_queue().  That callback reads active_queues and
dereferences scrqs without any lock, so the window between the guard
check and the actual use can race with the teardown, leading to:

  - divide-by-zero in '(qidx - 1) % vhost->nvme_scrqs.active_queues'
    when active_queues has been zeroed concurrently, and
  - NULL pointer dereference on 'vhost->nvme_scrqs.scrqs[qhandle->index]'
    when scrqs has been freed and set to NULL concurrently.

ibmvfc_nvme_unregister() already calls nvme_fc_unregister_localport()
followed by wait_for_completion(), which blocks until the transport has
fully quiesced all queue operations.  It just needs to happen before the
sub-CRQ memory is released.

Move ibmvfc_nvme_unregister() before ibmvfc_release_sub_crqs() in
ibmvfc_remove() so the transport is quiesced before the sub-CRQ arrays
are freed.  The reset path (ibmvfc_reset_crq) is unaffected: it only
deregisters sub-CRQs at the hypervisor level and never frees them, so
active_queues and scrqs remain valid across a reset.

Fixes: 86e495358096 ("scsi: ibmvfc: implement LLDD callbacks for mapping 
nvme-fc queues")
Signed-off-by: Tyrel Datwyler <[email protected]>
---
 drivers/scsi/ibmvscsi/ibmvfc-core.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c 
b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index e68ed5e79e58..701a9ebf2b7f 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -7014,11 +7014,10 @@ static void ibmvfc_remove(struct vio_dev *vdev)
        list_splice_init(&vhost->purge, &purge);
        spin_unlock_irqrestore(&vhost->host->host_lock, flags);
        ibmvfc_complete_purge(&purge);
+       ibmvfc_nvme_unregister(vhost);
        ibmvfc_release_sub_crqs(vhost);
        ibmvfc_release_crq_queue(vhost);
 
-       ibmvfc_nvme_unregister(vhost);
-
        ibmvfc_free_mem(vhost);
        spin_lock(&ibmvfc_driver_lock);
        list_del(&vhost->queue);
-- 
2.55.0


Reply via email to