scsi: lpfc: Fix use-after-free on unused nodes after port swap
authorJames Smart <jsmart2021@gmail.com>
Mon, 12 Apr 2021 01:31:18 +0000 (18:31 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 13 Apr 2021 05:39:13 +0000 (01:39 -0400)
During target port swap, the swap logic ignores the DROPPED flag in the
nodes. As a node then moves into the UNUSED state, the reference count will
be dropped. If a node is later reused and moved out of the UNUSED state, an
access can result in a use-after-free assert.

Fix by having the port swap logic propagate the DROPPED flag when switching
nodes. This will avoid reference from being dropped.

Link: https://lore.kernel.org/r/20210412013127.2387-8-jsmart2021@gmail.com
Co-developed-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_els.c

index ed57d92..c919bed 100644 (file)
@@ -1691,6 +1691,15 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
        else
                new_ndlp->nlp_flag &= ~NLP_RPI_REGISTERED;
 
+       /*
+        * Retain the DROPPED flag. This will take care of the init
+        * refcount when affecting the state change
+        */
+       if (keep_new_nlp_flag & NLP_DROPPED)
+               new_ndlp->nlp_flag |= NLP_DROPPED;
+       else
+               new_ndlp->nlp_flag &= ~NLP_DROPPED;
+
        ndlp->nlp_flag = keep_new_nlp_flag;
 
        /* if ndlp had NLP_UNREG_INP set, keep it */
@@ -1705,6 +1714,15 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
        else
                ndlp->nlp_flag &= ~NLP_RPI_REGISTERED;
 
+       /*
+        * Retain the DROPPED flag. This will take care of the init
+        * refcount when affecting the state change
+        */
+       if (keep_nlp_flag & NLP_DROPPED)
+               ndlp->nlp_flag |= NLP_DROPPED;
+       else
+               ndlp->nlp_flag &= ~NLP_DROPPED;
+
        spin_unlock_irq(&new_ndlp->lock);
        spin_unlock_irq(&ndlp->lock);