mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-09 04:10:18 +09:00
scsi: lpfc: Fix double free in lpfc_cmpl_els_logo_acc() caused by lpfc_nlp_not_used()
commit97f975823fupstream. Smatch detected a double free path because lpfc_nlp_not_used() releases an ndlp object before reaching lpfc_nlp_put() at the end of lpfc_cmpl_els_logo_acc(). Remove the outdated lpfc_nlp_not_used() routine. In lpfc_mbx_cmpl_ns_reg_login(), replace the call with lpfc_nlp_put(). In lpfc_cmpl_els_logo_acc(), replace the call with lpfc_unreg_rpi() and keep the lpfc_nlp_put() at the end of the routine. If ndlp's rpi was registered, then lpfc_unreg_rpi()'s completion routine performs the final ndlp clean up after lpfc_nlp_put() is called from lpfc_cmpl_els_logo_acc(). Otherwise if ndlp has no rpi registered, the lpfc_nlp_put() at the end of lpfc_cmpl_els_logo_acc() is the final ndlp clean up. Fixes:4430f7fd09("scsi: lpfc: Rework locations of ndlp reference taking") Cc: <stable@vger.kernel.org> # v5.11+ Reported-by: Dan Carpenter <error27@gmail.com> Link: https://lore.kernel.org/all/Y3OefhyyJNKH%2Fiaf@kili/ Signed-off-by: Justin Tee <justin.tee@broadcom.com> Link: https://lore.kernel.org/r/20230417191558.83100-3-justintee8345@gmail.com Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
7adcc32eb5
commit
6436ca035b
@@ -134,7 +134,6 @@ void lpfc_check_nlp_post_devloss(struct lpfc_vport *vport,
|
|||||||
struct lpfc_nodelist *ndlp);
|
struct lpfc_nodelist *ndlp);
|
||||||
void lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
void lpfc_ignore_els_cmpl(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||||
struct lpfc_iocbq *rspiocb);
|
struct lpfc_iocbq *rspiocb);
|
||||||
int lpfc_nlp_not_used(struct lpfc_nodelist *ndlp);
|
|
||||||
struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_vport *, uint32_t);
|
struct lpfc_nodelist *lpfc_setup_disc_node(struct lpfc_vport *, uint32_t);
|
||||||
void lpfc_disc_list_loopmap(struct lpfc_vport *);
|
void lpfc_disc_list_loopmap(struct lpfc_vport *);
|
||||||
void lpfc_disc_start(struct lpfc_vport *);
|
void lpfc_disc_start(struct lpfc_vport *);
|
||||||
|
|||||||
@@ -5150,14 +5150,9 @@ lpfc_els_free_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *elsiocb)
|
|||||||
*
|
*
|
||||||
* This routine is the completion callback function to the Logout (LOGO)
|
* This routine is the completion callback function to the Logout (LOGO)
|
||||||
* Accept (ACC) Response ELS command. This routine is invoked to indicate
|
* Accept (ACC) Response ELS command. This routine is invoked to indicate
|
||||||
* the completion of the LOGO process. It invokes the lpfc_nlp_not_used() to
|
* the completion of the LOGO process. If the node has transitioned to NPR,
|
||||||
* release the ndlp if it has the last reference remaining (reference count
|
* this routine unregisters the RPI if it is still registered. The
|
||||||
* is 1). If succeeded (meaning ndlp released), it sets the iocb ndlp
|
* lpfc_els_free_iocb() is invoked to release the IOCB data structure.
|
||||||
* field to NULL to inform the following lpfc_els_free_iocb() routine no
|
|
||||||
* ndlp reference count needs to be decremented. Otherwise, the ndlp
|
|
||||||
* reference use-count shall be decremented by the lpfc_els_free_iocb()
|
|
||||||
* routine. Finally, the lpfc_els_free_iocb() is invoked to release the
|
|
||||||
* IOCB data structure.
|
|
||||||
**/
|
**/
|
||||||
static void
|
static void
|
||||||
lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
||||||
@@ -5198,19 +5193,9 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
(ndlp->nlp_last_elscmd == ELS_CMD_PLOGI))
|
(ndlp->nlp_last_elscmd == ELS_CMD_PLOGI))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* NPort Recovery mode or node is just allocated */
|
if (ndlp->nlp_flag & NLP_RPI_REGISTERED)
|
||||||
if (!lpfc_nlp_not_used(ndlp)) {
|
|
||||||
/* A LOGO is completing and the node is in NPR state.
|
|
||||||
* Just unregister the RPI because the node is still
|
|
||||||
* required.
|
|
||||||
*/
|
|
||||||
lpfc_unreg_rpi(vport, ndlp);
|
lpfc_unreg_rpi(vport, ndlp);
|
||||||
} else {
|
|
||||||
/* Indicate the node has already released, should
|
|
||||||
* not reference to it from within lpfc_els_free_iocb.
|
|
||||||
*/
|
|
||||||
cmdiocb->ndlp = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
/*
|
/*
|
||||||
@@ -5230,9 +5215,8 @@ lpfc_cmpl_els_logo_acc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|||||||
* RPI (Remote Port Index) mailbox command to the @phba. It simply releases
|
* RPI (Remote Port Index) mailbox command to the @phba. It simply releases
|
||||||
* the associated lpfc Direct Memory Access (DMA) buffer back to the pool and
|
* the associated lpfc Direct Memory Access (DMA) buffer back to the pool and
|
||||||
* decrements the ndlp reference count held for this completion callback
|
* decrements the ndlp reference count held for this completion callback
|
||||||
* function. After that, it invokes the lpfc_nlp_not_used() to check
|
* function. After that, it invokes the lpfc_drop_node to check
|
||||||
* whether there is only one reference left on the ndlp. If so, it will
|
* whether it is appropriate to release the node.
|
||||||
* perform one more decrement and trigger the release of the ndlp.
|
|
||||||
**/
|
**/
|
||||||
void
|
void
|
||||||
lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
|
lpfc_mbx_cmpl_dflt_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
|
||||||
|
|||||||
@@ -4332,13 +4332,14 @@ out:
|
|||||||
|
|
||||||
/* If the node is not registered with the scsi or nvme
|
/* If the node is not registered with the scsi or nvme
|
||||||
* transport, remove the fabric node. The failed reg_login
|
* transport, remove the fabric node. The failed reg_login
|
||||||
* is terminal.
|
* is terminal and forces the removal of the last node
|
||||||
|
* reference.
|
||||||
*/
|
*/
|
||||||
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
|
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
|
||||||
spin_lock_irq(&ndlp->lock);
|
spin_lock_irq(&ndlp->lock);
|
||||||
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
ndlp->nlp_flag &= ~NLP_NPR_2B_DISC;
|
||||||
spin_unlock_irq(&ndlp->lock);
|
spin_unlock_irq(&ndlp->lock);
|
||||||
lpfc_nlp_not_used(ndlp);
|
lpfc_nlp_put(ndlp);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
|
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
|
||||||
@@ -6703,25 +6704,6 @@ lpfc_nlp_put(struct lpfc_nodelist *ndlp)
|
|||||||
return ndlp ? kref_put(&ndlp->kref, lpfc_nlp_release) : 0;
|
return ndlp ? kref_put(&ndlp->kref, lpfc_nlp_release) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This routine free's the specified nodelist if it is not in use
|
|
||||||
* by any other discovery thread. This routine returns 1 if the
|
|
||||||
* ndlp has been freed. A return value of 0 indicates the ndlp is
|
|
||||||
* not yet been released.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
lpfc_nlp_not_used(struct lpfc_nodelist *ndlp)
|
|
||||||
{
|
|
||||||
lpfc_debugfs_disc_trc(ndlp->vport, LPFC_DISC_TRC_NODE,
|
|
||||||
"node not used: did:x%x flg:x%x refcnt:x%x",
|
|
||||||
ndlp->nlp_DID, ndlp->nlp_flag,
|
|
||||||
kref_read(&ndlp->kref));
|
|
||||||
|
|
||||||
if (kref_read(&ndlp->kref) == 1)
|
|
||||||
if (lpfc_nlp_put(ndlp))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lpfc_fcf_inuse - Check if FCF can be unregistered.
|
* lpfc_fcf_inuse - Check if FCF can be unregistered.
|
||||||
* @phba: Pointer to hba context object.
|
* @phba: Pointer to hba context object.
|
||||||
|
|||||||
Reference in New Issue
Block a user