From: Justin Tee <justintee8345@gmail.com>
To: linux-scsi@vger.kernel.org
Cc: jsmart833426@gmail.com, justin.tee@broadcom.com,
Justin Tee <justintee8345@gmail.com>
Subject: [PATCH v3 06/14] lpfc: Fix ndlp use-after-free during repeated RSCN and rediscovery sequence
Date: Mon, 13 Jul 2026 18:18:04 -0700 [thread overview]
Message-ID: <20260714011812.106753-7-justintee8345@gmail.com> (raw)
In-Reply-To: <20260714011812.106753-1-justintee8345@gmail.com>
In large SAN configurations when a target port fails over, RSCNs may be
spammed triggering a repeat of restarting discovery events for an ndlp
object.
In the case when discovery reaches PRLI state, but the PRLI operation is
interrupted, this leaves the nlp_fc4_type and nlp_type flags cleared. And,
on the next cycle through lpfc_nlp_reg_node, the NLP_XPT_REGD flag is set
but registraton with the fc transport is bypassed because
lpfc_valid_xpt_node returns false.
This sets up a condition whereby the next call to lpfc_nlp_unreg_node
results in a premature release of the ndlp, and a callback from the
transport results in a use-after-free condition.
To address this issue, refactor lpfc_fc4_xpt_flags such that both SCSI and
NVME have separate flags indicating registration with their respective
transport. The flags also indicate a request to unregister had been made.
In dev-loss or transport callback processing, the SCSI_XPT_UNREG_WAIT and
NVME_XPT_UNREG_WAIT flags indicate whether the ndlp reference has already
been released.
Signed-off-by: Justin Tee <justintee8345@gmail.com>
---
drivers/scsi/lpfc/lpfc_disc.h | 2 +-
drivers/scsi/lpfc/lpfc_hbadisc.c | 97 ++++++++++++++++----------------
2 files changed, 50 insertions(+), 49 deletions(-)
diff --git a/drivers/scsi/lpfc/lpfc_disc.h b/drivers/scsi/lpfc/lpfc_disc.h
index a377e97cbe65..afc5e84bf0fa 100644
--- a/drivers/scsi/lpfc/lpfc_disc.h
+++ b/drivers/scsi/lpfc/lpfc_disc.h
@@ -83,7 +83,7 @@ struct lpfc_enc_info {
};
enum lpfc_fc4_xpt_flags {
- NLP_XPT_REGD = 0x1,
+ SCSI_XPT_UNREG_WAIT = 0x1,
SCSI_XPT_REGD = 0x2,
NVME_XPT_REGD = 0x4,
NVME_XPT_UNREG_WAIT = 0x8,
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index c9b02d2c6305..6337dae4f1cd 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -200,26 +200,18 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
/* The scsi_transport is done with the rport so lpfc cannot
* call to unregister.
*/
- if (ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
+ if ((ndlp->fc4_xpt_flags & SCSI_XPT_REGD) &&
+ !(ndlp->fc4_xpt_flags & SCSI_XPT_UNREG_WAIT)) {
+ /* Reference held since no unreg call made */
ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
+ spin_unlock_irqrestore(&ndlp->lock, iflags);
- /* If NLP_XPT_REGD was cleared in lpfc_nlp_unreg_node,
- * unregister calls were made to the scsi and nvme
- * transports and refcnt was already decremented. Clear
- * the NLP_XPT_REGD flag only if the NVME nrport is
- * confirmed unregistered.
- */
- if (ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
- if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
- ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
- spin_unlock_irqrestore(&ndlp->lock, iflags);
-
- /* Release scsi transport reference */
- lpfc_nlp_put(ndlp);
- } else {
- spin_unlock_irqrestore(&ndlp->lock, iflags);
- }
+ /* Release scsi transport reference */
+ lpfc_nlp_put(ndlp);
} else {
+ /* Clear scsi xpt flags */
+ ndlp->fc4_xpt_flags &= ~(SCSI_XPT_REGD |
+ SCSI_XPT_UNREG_WAIT);
spin_unlock_irqrestore(&ndlp->lock, iflags);
}
@@ -270,7 +262,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
* The backend does not expect any more calls associated with this
* rport. Remove the association between rport and ndlp.
*/
- ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
+ ndlp->fc4_xpt_flags &= ~(SCSI_XPT_REGD | SCSI_XPT_UNREG_WAIT);
((struct lpfc_rport_data *)rport->dd_data)->pnode = NULL;
ndlp->rport = NULL;
spin_unlock_irqrestore(&ndlp->lock, iflags);
@@ -606,7 +598,7 @@ lpfc_dev_loss_tmo_handler(struct lpfc_nodelist *ndlp)
return fcf_inuse;
}
- if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD))
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)))
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
return fcf_inuse;
@@ -4346,7 +4338,8 @@ lpfc_mbx_cmpl_ns_reg_login(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
*/
if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
clear_bit(NLP_NPR_2B_DISC, &ndlp->nlp_flag);
- lpfc_nlp_put(ndlp);
+ if (!test_and_set_bit(NLP_DROPPED, &ndlp->nlp_flag))
+ lpfc_nlp_put(ndlp);
}
if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) {
@@ -4527,6 +4520,7 @@ lpfc_register_remote_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
}
spin_lock_irqsave(&ndlp->lock, flags);
+ ndlp->fc4_xpt_flags &= ~SCSI_XPT_UNREG_WAIT;
ndlp->fc4_xpt_flags |= SCSI_XPT_REGD;
spin_unlock_irqrestore(&ndlp->lock, flags);
@@ -4562,6 +4556,7 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
{
struct fc_rport *rport = ndlp->rport;
struct lpfc_vport *vport = ndlp->vport;
+ unsigned long flags;
if (vport->cfg_enable_fc4_type == LPFC_ENABLE_NVME)
return;
@@ -4577,6 +4572,11 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
kref_read(&ndlp->kref));
fc_remote_port_delete(rport);
+
+ /* Flag unreg pending and reference released */
+ spin_lock_irqsave(&ndlp->lock, flags);
+ ndlp->fc4_xpt_flags |= SCSI_XPT_UNREG_WAIT;
+ spin_unlock_irqrestore(&ndlp->lock, flags);
lpfc_nlp_put(ndlp);
}
@@ -4623,7 +4623,9 @@ lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
lpfc_check_nlp_post_devloss(vport, ndlp);
spin_lock_irqsave(&ndlp->lock, iflags);
- if (ndlp->fc4_xpt_flags & NLP_XPT_REGD) {
+ if ((ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD)) &&
+ !(ndlp->fc4_xpt_flags & (SCSI_XPT_UNREG_WAIT |
+ NVME_XPT_UNREG_WAIT))) {
/* Already registered with backend, trigger rescan */
spin_unlock_irqrestore(&ndlp->lock, iflags);
@@ -4633,16 +4635,11 @@ lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
}
return;
}
-
- ndlp->fc4_xpt_flags |= NLP_XPT_REGD;
spin_unlock_irqrestore(&ndlp->lock, iflags);
if (lpfc_valid_xpt_node(ndlp)) {
vport->phba->nport_event_cnt++;
- /*
- * Tell the fc transport about the port, if we haven't
- * already. If we have, and it's a scsi entity, be
- */
+ /* Tell the fc transport about the port */
lpfc_register_remote_port(vport, ndlp);
}
@@ -4650,24 +4647,24 @@ lpfc_nlp_reg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
if (!(ndlp->nlp_fc4_type & NLP_FC4_NVME))
return;
+ if (vport->phba->sli_rev < LPFC_SLI_REV4)
+ return;
+
/* Notify the NVME transport of this new rport. */
- if (vport->phba->sli_rev >= LPFC_SLI_REV4 &&
- ndlp->nlp_fc4_type & NLP_FC4_NVME) {
- if (vport->phba->nvmet_support == 0) {
- /* Register this rport with the transport.
- * Only NVME Target Rports are registered with
- * the transport.
- */
- if (ndlp->nlp_type & NLP_NVME_TARGET) {
- vport->phba->nport_event_cnt++;
- lpfc_nvme_register_port(vport, ndlp);
- }
- } else {
- /* Just take an NDLP ref count since the
- * target does not register rports.
- */
- lpfc_nlp_get(ndlp);
+ if (vport->phba->nvmet_support == 0) {
+ /* Register this rport with the transport.
+ * Only NVME Target Rports are registered with
+ * the transport.
+ */
+ if (ndlp->nlp_type & NLP_NVME_TARGET) {
+ vport->phba->nport_event_cnt++;
+ lpfc_nvme_register_port(vport, ndlp);
}
+ } else {
+ /* Just take an NDLP ref count since the
+ * target does not register rports.
+ */
+ lpfc_nlp_get(ndlp);
}
}
@@ -4678,7 +4675,7 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
unsigned long iflags;
spin_lock_irqsave(&ndlp->lock, iflags);
- if (!(ndlp->fc4_xpt_flags & NLP_XPT_REGD)) {
+ if (!(ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | NVME_XPT_REGD))) {
spin_unlock_irqrestore(&ndlp->lock, iflags);
lpfc_printf_vlog(vport, KERN_INFO,
LOG_ELS | LOG_NODE | LOG_DISCOVERY,
@@ -4688,12 +4685,11 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
ndlp->nlp_flag, ndlp->fc4_xpt_flags);
return;
}
-
- ndlp->fc4_xpt_flags &= ~NLP_XPT_REGD;
spin_unlock_irqrestore(&ndlp->lock, iflags);
if (ndlp->rport &&
- ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {
+ ((ndlp->fc4_xpt_flags & (SCSI_XPT_REGD | SCSI_XPT_UNREG_WAIT)) ==
+ SCSI_XPT_REGD)) {
vport->phba->nport_event_cnt++;
lpfc_unregister_remote_port(ndlp);
} else if (!ndlp->rport) {
@@ -4706,7 +4702,12 @@ lpfc_nlp_unreg_node(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
kref_read(&ndlp->kref));
}
- if (ndlp->fc4_xpt_flags & NVME_XPT_REGD) {
+ /* If no remote NVME node is indicated, just exit */
+ if (!(ndlp->nlp_fc4_type & NLP_FC4_NVME))
+ return;
+
+ if ((ndlp->fc4_xpt_flags & (NVME_XPT_REGD | NVME_XPT_UNREG_WAIT)) ==
+ NVME_XPT_REGD) {
vport->phba->nport_event_cnt++;
if (vport->phba->nvmet_support == 0) {
lpfc_nvme_unregister_port(vport, ndlp);
--
2.38.0
next prev parent reply other threads:[~2026-07-14 0:37 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-14 1:17 [PATCH v3 00/14] Update lpfc to revision 15.0.0.1 Justin Tee
2026-07-14 1:17 ` [PATCH v3 01/14] lpfc: Fix use-after-free in lpfc_cmpl_ct_cmd_vmid Justin Tee
2026-07-14 0:51 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 02/14] lpfc: Early return out of lpfc_els_abort when HBA_SETUP flag is not set Justin Tee
2026-07-14 0:53 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 03/14] lpfc: Fix kernel oops when unmapping scsi dma buffers for an aborted cmd Justin Tee
2026-07-14 0:57 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 04/14] lpfc: Check fc4_xpt_flags before decrementing ndlp kref on FDISC error Justin Tee
2026-07-14 0:55 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 05/14] lpfc: Add handling for when PLOGI or PRLI is dropped during link failure Justin Tee
2026-07-14 1:02 ` sashiko-bot
2026-07-14 1:18 ` Justin Tee [this message]
2026-07-14 0:50 ` [PATCH v3 06/14] lpfc: Fix ndlp use-after-free during repeated RSCN and rediscovery sequence sashiko-bot
2026-07-14 1:18 ` [PATCH v3 07/14] lpfc: Rework I/O flush ordering when unloading driver Justin Tee
2026-07-14 1:00 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 08/14] lpfc: Improve PLOGI retry handling for large SAN configurations Justin Tee
2026-07-14 0:55 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 09/14] lpfc: Send inhibited ABORT_WQE when PLOGI CQE SEQUENCE_TMO is received Justin Tee
2026-07-14 1:05 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 10/14] lpfc: Remove slowpath cqe process limiter in slow ring event handler Justin Tee
2026-07-14 0:54 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 11/14] lpfc: Put iocbq on phba->txq when ELS WQ is full or ELS SGL unavailable Justin Tee
2026-07-14 0:50 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 12/14] lpfc: Update ELS ACC logging for diagnostic troubleshooting Justin Tee
2026-07-14 1:02 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 13/14] lpfc: Refactor calls on fc_disctmo to lpfc_set_disctmo in RSCN handler Justin Tee
2026-07-14 1:00 ` sashiko-bot
2026-07-14 1:18 ` [PATCH v3 14/14] lpfc: Update lpfc version to 15.0.0.1 Justin Tee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260714011812.106753-7-justintee8345@gmail.com \
--to=justintee8345@gmail.com \
--cc=jsmart833426@gmail.com \
--cc=justin.tee@broadcom.com \
--cc=linux-scsi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.