scsi: lpfc: Fix null pointer dereference after failing to issue FLOGI and PLOGI
commit 577a942df3 upstream.
If lpfc_issue_els_flogi() fails and returns non-zero status, the node
reference count is decremented to trigger the release of the nodelist
structure. However, if there is a prior registration or dev-loss-evt work
pending, the node may be released prematurely. When dev-loss-evt
completes, the released node is referenced causing a use-after-free null
pointer dereference.
Similarly, when processing non-zero ELS PLOGI completion status in
lpfc_cmpl_els_plogi(), the ndlp flags are checked for a transport
registration before triggering node removal. If dev-loss-evt work is
pending, the node may be released prematurely and a subsequent call to
lpfc_dev_loss_tmo_handler() results in a use after free ndlp dereference.
Add test for pending dev-loss before decrementing the node reference count
for FLOGI, PLOGI, PRLI, and ADISC handling.
Link: https://lore.kernel.org/r/20220412222008.126521-9-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>
[Minor context change fixed.]
Signed-off-by: Bin Lan <bin.lan.cn@windriver.com>
Signed-off-by: He Zhe <zhe.he@windriver.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
984212fa6b
commit
c7dc74ab79
@@ -1517,10 +1517,13 @@ lpfc_initial_flogi(struct lpfc_vport *vport)
|
||||
}
|
||||
|
||||
if (lpfc_issue_els_flogi(vport, ndlp, 0)) {
|
||||
/* This decrement of reference count to node shall kick off
|
||||
* the release of the node.
|
||||
/* A node reference should be retained while registered with a
|
||||
* transport or dev-loss-evt work is pending.
|
||||
* Otherwise, decrement node reference to trigger release.
|
||||
*/
|
||||
lpfc_nlp_put(ndlp);
|
||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
|
||||
!(ndlp->nlp_flag & NLP_IN_DEV_LOSS))
|
||||
lpfc_nlp_put(ndlp);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -1563,10 +1566,13 @@ lpfc_initial_fdisc(struct lpfc_vport *vport)
|
||||
}
|
||||
|
||||
if (lpfc_issue_els_fdisc(vport, ndlp, 0)) {
|
||||
/* decrement node reference count to trigger the release of
|
||||
* the node.
|
||||
/* A node reference should be retained while registered with a
|
||||
* transport or dev-loss-evt work is pending.
|
||||
* Otherwise, decrement node reference to trigger release.
|
||||
*/
|
||||
lpfc_nlp_put(ndlp);
|
||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
|
||||
!(ndlp->nlp_flag & NLP_IN_DEV_LOSS))
|
||||
lpfc_nlp_put(ndlp);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
@@ -1967,6 +1973,7 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
struct lpfc_dmabuf *prsp;
|
||||
int disc;
|
||||
struct serv_parm *sp = NULL;
|
||||
bool release_node = false;
|
||||
|
||||
/* we pass cmdiocb to state machine which needs rspiocb as well */
|
||||
cmdiocb->context_un.rsp_iocb = rspiocb;
|
||||
@@ -2047,19 +2054,21 @@ lpfc_cmpl_els_plogi(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
goto out;
|
||||
}
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
|
||||
/* No PLOGI collision and the node is not registered with the
|
||||
* scsi or nvme transport. It is no longer an active node. Just
|
||||
* start the device remove process.
|
||||
*/
|
||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
|
||||
spin_lock_irq(&ndlp->lock);
|
||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS))
|
||||
release_node = true;
|
||||
}
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
|
||||
if (release_node)
|
||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
||||
NLP_EVT_DEVICE_RM);
|
||||
}
|
||||
} else {
|
||||
/* Good status, call state machine */
|
||||
prsp = list_entry(((struct lpfc_dmabuf *)
|
||||
@@ -2269,6 +2278,7 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
struct lpfc_nodelist *ndlp;
|
||||
char *mode;
|
||||
u32 loglevel;
|
||||
bool release_node = false;
|
||||
|
||||
/* we pass cmdiocb to state machine which needs rspiocb as well */
|
||||
cmdiocb->context_un.rsp_iocb = rspiocb;
|
||||
@@ -2335,14 +2345,18 @@ lpfc_cmpl_els_prli(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
* it is no longer an active node. Otherwise devloss
|
||||
* handles the final cleanup.
|
||||
*/
|
||||
spin_lock_irq(&ndlp->lock);
|
||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
|
||||
!ndlp->fc4_prli_sent) {
|
||||
spin_lock_irq(&ndlp->lock);
|
||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS))
|
||||
release_node = true;
|
||||
}
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
|
||||
if (release_node)
|
||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
||||
NLP_EVT_DEVICE_RM);
|
||||
}
|
||||
} else {
|
||||
/* Good status, call state machine. However, if another
|
||||
* PRLI is outstanding, don't call the state machine
|
||||
@@ -2713,6 +2727,7 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
IOCB_t *irsp;
|
||||
struct lpfc_nodelist *ndlp;
|
||||
int disc;
|
||||
bool release_node = false;
|
||||
|
||||
/* we pass cmdiocb to state machine which needs rspiocb as well */
|
||||
cmdiocb->context_un.rsp_iocb = rspiocb;
|
||||
@@ -2771,13 +2786,17 @@ lpfc_cmpl_els_adisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||
* transport, it is no longer an active node. Otherwise
|
||||
* devloss handles the final cleanup.
|
||||
*/
|
||||
spin_lock_irq(&ndlp->lock);
|
||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
|
||||
spin_lock_irq(&ndlp->lock);
|
||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
if (!(ndlp->nlp_flag & NLP_IN_DEV_LOSS))
|
||||
release_node = true;
|
||||
}
|
||||
spin_unlock_irq(&ndlp->lock);
|
||||
|
||||
if (release_node)
|
||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
||||
NLP_EVT_DEVICE_RM);
|
||||
}
|
||||
} else
|
||||
/* Good status, call state machine */
|
||||
lpfc_disc_state_machine(vport, ndlp, cmdiocb,
|
||||
|
||||
Reference in New Issue
Block a user