]> git.hungrycats.org Git - linux/commitdiff
scsi: lpfc: Validate hdwq pointers before dereferencing in reset/errata paths
authorJustin Tee <justin.tee@broadcom.com>
Fri, 26 Jul 2024 23:15:07 +0000 (16:15 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 10 Oct 2024 10:00:35 +0000 (12:00 +0200)
[ Upstream commit 2be1d4f11944cd6283cb97268b3e17c4424945ca ]

When the HBA is undergoing a reset or is handling an errata event, NULL ptr
dereference crashes may occur in routines such as
lpfc_sli_flush_io_rings(), lpfc_dev_loss_tmo_callbk(), or
lpfc_abort_handler().

Add NULL ptr checks before dereferencing hdwq pointers that may have been
freed due to operations colliding with a reset or errata event handler.

Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Link: https://lore.kernel.org/r/20240726231512.92867-4-justintee8345@gmail.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_sli.c

index 13b08c85440fec5beae218c479aeb9b33edaa3f7..e74a676b6e153642e11d831a6be612c7f260cc2c 100644 (file)
@@ -175,7 +175,8 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
                         ndlp->nlp_state, ndlp->fc4_xpt_flags);
 
        /* Don't schedule a worker thread event if the vport is going down. */
-       if (test_bit(FC_UNLOADING, &vport->load_flag)) {
+       if (test_bit(FC_UNLOADING, &vport->load_flag) ||
+           !test_bit(HBA_SETUP, &phba->hba_flag)) {
                spin_lock_irqsave(&ndlp->lock, iflags);
                ndlp->rport = NULL;
 
index 9f0b59672e1915d98ddee8737a5fdc4198c65b70..0eaede8275dac40c2d9fd682898ab87c9d14b58e 100644 (file)
@@ -5555,11 +5555,20 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
 
        iocb = &lpfc_cmd->cur_iocbq;
        if (phba->sli_rev == LPFC_SLI_REV4) {
-               pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq->pring;
-               if (!pring_s4) {
+               /* if the io_wq & pring are gone, the port was reset. */
+               if (!phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq ||
+                   !phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq->pring) {
+                       lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
+                                        "2877 SCSI Layer I/O Abort Request "
+                                        "IO CMPL Status x%x ID %d LUN %llu "
+                                        "HBA_SETUP %d\n", FAILED,
+                                        cmnd->device->id,
+                                        (u64)cmnd->device->lun,
+                                        test_bit(HBA_SETUP, &phba->hba_flag));
                        ret = FAILED;
                        goto out_unlock_hba;
                }
+               pring_s4 = phba->sli4_hba.hdwq[iocb->hba_wqidx].io_wq->pring;
                spin_lock(&pring_s4->ring_lock);
        }
        /* the command is in process of being cancelled */
index 3e55d5edd60abc289d953cff5e7f3da150097c2b..c6fcaeeb529454f5ee69e2b7218673bf69a63d4d 100644 (file)
@@ -4687,6 +4687,17 @@ lpfc_sli_flush_io_rings(struct lpfc_hba *phba)
        /* Look on all the FCP Rings for the iotag */
        if (phba->sli_rev >= LPFC_SLI_REV4) {
                for (i = 0; i < phba->cfg_hdw_queue; i++) {
+                       if (!phba->sli4_hba.hdwq ||
+                           !phba->sli4_hba.hdwq[i].io_wq) {
+                               lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+                                               "7777 hdwq's deleted %lx "
+                                               "%lx %x %x\n",
+                                               phba->pport->load_flag,
+                                               phba->hba_flag,
+                                               phba->link_state,
+                                               phba->sli.sli_flag);
+                               return;
+                       }
                        pring = phba->sli4_hba.hdwq[i].io_wq->pring;
 
                        spin_lock_irq(&pring->ring_lock);