Re: [PATCH 1/1] scsi: lpfc: Clear rport association before unregistering it

[email protected] Wed, 29 Jul 2026 15:33:25 +0000
Newsgroups org.kernel.vger.linux-scsi
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] Unconditionally clearing `rdata->pnode` breaks the devloss timeout state machine, leading to permanent `lpfc_nodelist` memory leaks for SCSI targets.

Pre-existing issues:
- [High] A TOCTOU race condition from lockless checks of `ndlp->rport` leads to a NULL pointer dereference of `rport->dd_data` in `lpfc_unregister_remote_port()`.
- [High] The patch attempts to fix a Use-After-Free (UAF) by clearing `rdata->pnode` under `ndlp->lock`, but leaves a TOCTOU race window because the read side in `lpfc_dev_loss_tmo_callbk()` is completely lockless.
--

--- Patch [1]: [PATCH 1/1] scsi: lpfc: Clear rport association before unregistering it ---
commit 97fd261025f15460afb6d81b854ab25279c37bc0
Author: Dai Ngo <[email protected]>

scsi: lpfc: Clear rport association before unregistering it

System crashed when a Fibre Channel cable is removed from an Emulex/Broadcom
lpfc HBA. This crash occurs after the lpfc Link Down event, after dm-multipath
fails paths, and when the FC remote ports time out and are removed. This patch
clears rdata->pnode and ndlp->rport under ndlp->lock before calling
fc_remote_port_delete() to prevent stale rport-to-node references.

> diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
> index c9b02d2c63054..a4e9a60ec162b 100644
> --- a/drivers/scsi/lpfc/lpfc_hbadisc.c
> +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
[ ... ]
> @@ -4576,6 +4578,14 @@ lpfc_unregister_remote_port(struct lpfc_nodelist *ndlp)
>  			 ndlp->nlp_DID, rport, ndlp->fc4_xpt_flags,
>  			 kref_read(&ndlp->kref));
>  
> +	rdata = rport->dd_data;

[Severity: High]
This is a pre-existing issue, but won't this cause a NULL pointer dereference
if rport is NULL?

Looking at lpfc_nlp_unreg_node() in drivers/scsi/lpfc/lpfc_hbadisc.c,
ndlp->rport is checked outside of ndlp->lock:

    if (ndlp->rport &&
        ndlp->fc4_xpt_flags & SCSI_XPT_REGD) {

If a concurrent lpfc_dev_loss_tmo_callbk() acquires ndlp->lock and sets
ndlp->rport to NULL, lpfc_unregister_remote_port() will initialize its local
rport variable to NULL, leading to a crash here when dereferencing
rport->dd_data.

> +	spin_lock_irqsave(&ndlp->lock, iflags);
> +	if (rdata && rdata->pnode == ndlp)
> +		rdata->pnode = NULL;

[Severity: High]
Does unconditionally clearing rdata->pnode here break the devloss timeout
state machine and cause a permanent node leak?

Before this patch, fc_remote_port_delete() starts the SCSI transport's
devloss timer, which eventually calls lpfc_dev_loss_tmo_callbk(). That callback
relies on rdata->pnode to locate the ndlp and queue an LPFC_EVT_DEV_LOSS event,
which drops the initial node reference.

By setting rdata->pnode to NULL before deleting the rport, won't
lpfc_dev_loss_tmo_callbk() in drivers/scsi/lpfc/lpfc_hbadisc.c always see
a NULL pointer and return immediately:

    ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode;
    if (!ndlp)
        return;

And thus never drop the initial reference, leaking the lpfc_nodelist memory?

[Severity: High]
This isn't a bug introduced by this patch, but does clearing rdata->pnode
under ndlp->lock actually prevent the use-after-free race in the devloss
callback?

The devloss callback in drivers/scsi/lpfc/lpfc_hbadisc.c reads the pointer
locklessly and doesn't take a reference:

    ndlp = ((struct lpfc_rport_data *)rport->dd_data)->pnode;

If lpfc_dev_loss_tmo_callbk() reads ndlp and then is preempted, a concurrent
lpfc_unregister_remote_port() could clear rdata->pnode and call
lpfc_nlp_put(ndlp), freeing the node. When the callback resumes, it would
access ndlp->vport and attempt to acquire ndlp->lock on a freed object.

> +	ndlp->rport = NULL;
> +	ndlp->fc4_xpt_flags &= ~SCSI_XPT_REGD;
> +	spin_unlock_irqrestore(&ndlp->lock, iflags);
> +
>  	fc_remote_port_delete(rport);
>  	lpfc_nlp_put(ndlp);
>  }

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=1