ibmvfc_remove() tears down the CRQ and frees all host memory but never
calls ibmvfc_nvme_unregister().  The NVMe-FC transport therefore retains
a live reference to the local port whose private pointer points into the
now-freed ibmvfc_host, leading to a use-after-free whenever the transport
subsequently touches the port (queue creation, port scanning, etc.).

ibmvfc_nvme_unregister() is already called from the NPIV logout and host
reset paths during normal operation; driver removal simply missed it.

Add the call after ibmvfc_release_crq_queue() — at which point the CRQ
is torn down so no further completions can arrive — and before
ibmvfc_free_mem(), so host memory is still valid while
nvme_fc_unregister_localport() runs and waits for the
localport_delete callback to complete.

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 | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/drivers/scsi/ibmvscsi/ibmvfc-core.c 
b/drivers/scsi/ibmvscsi/ibmvfc-core.c
index 080312ff0a93..e68ed5e79e58 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc-core.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc-core.c
@@ -6976,6 +6976,7 @@ static int ibmvfc_probe(struct vio_dev *vdev, const 
struct vio_device_id *id)
 static void ibmvfc_remove(struct vio_dev *vdev)
 {
        struct ibmvfc_host *vhost = dev_get_drvdata(&vdev->dev);
+       struct ibmvfc_target *tgt, *tgt_tmp;
        LIST_HEAD(purge);
        unsigned long flags;
 
@@ -6992,6 +6993,22 @@ static void ibmvfc_remove(struct vio_dev *vdev)
        fc_remove_host(vhost->host);
        scsi_remove_host(vhost->host);
 
+       list_for_each_entry_safe(tgt, tgt_tmp, &vhost->nvme_scrqs.targets, 
queue) {
+               if (tgt->nvme_remote_port)
+                       ibmvfc_nvme_unregister_remoteport(tgt);
+               list_del(&tgt->queue);
+               timer_delete_sync(&tgt->timer);
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
+       }
+
+       list_for_each_entry_safe(tgt, tgt_tmp, &vhost->scsi_scrqs.targets, 
queue) {
+               if (tgt->rport)
+                       fc_remote_port_delete(tgt->rport);
+               list_del(&tgt->queue);
+               timer_delete_sync(&tgt->timer);
+               kref_put(&tgt->kref, ibmvfc_release_tgt);
+       }
+
        spin_lock_irqsave(&vhost->host->host_lock, flags);
        ibmvfc_purge_requests(vhost, DID_ERROR);
        list_splice_init(&vhost->purge, &purge);
@@ -7000,6 +7017,8 @@ static void ibmvfc_remove(struct vio_dev *vdev)
        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