Skip to content

Commit dae40be

Browse files
Justin Teemartinkpetersen
authored andcommitted
scsi: lpfc: Prevent use-after-free during rmmod with mapped NVMe rports
During rmmod, when dev_loss_tmo callback is called, an ndlp kref count is decremented twice. Once for SCSI transport registration and second to remove the initial node allocation kref. If there is also an NVMe transport registration, another reference count decrement is expected in lpfc_nvme_unregister_port(). Race conditions between the NVMe transport remoteport_delete and dev_loss_tmo callbacks sometimes results in premature ndlp object release resulting in use-after-free issues. Fix by not dropping the ndlp object in dev_loss_tmo callback with an outstanding NVMe transport registration. Inversely, mark the final NLP_DROPPED flag in lpfc_nvme_unregister_port when rmmod flag is set. Signed-off-by: Justin Tee <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 9c30349 commit dae40be

File tree

2 files changed

+19
-8
lines changed

2 files changed

+19
-8
lines changed

drivers/scsi/lpfc/lpfc_hbadisc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,8 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
199199
/* Only 1 thread can drop the initial node reference. If
200200
* another thread has set NLP_DROPPED, this thread is done.
201201
*/
202-
if (!(ndlp->nlp_flag & NLP_DROPPED)) {
202+
if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD) &&
203+
!(ndlp->nlp_flag & NLP_DROPPED)) {
203204
ndlp->nlp_flag |= NLP_DROPPED;
204205
spin_unlock_irqrestore(&ndlp->lock, iflags);
205206
lpfc_nlp_put(ndlp);

drivers/scsi/lpfc/lpfc_nvme.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,7 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport)
228228
spin_unlock_irq(&ndlp->lock);
229229

230230
/* On a devloss timeout event, one more put is executed provided the
231-
* NVME and SCSI rport unregister requests are complete. If the vport
232-
* is unloading, this extra put is executed by lpfc_drop_node.
231+
* NVME and SCSI rport unregister requests are complete.
233232
*/
234233
if (!(ndlp->fc4_xpt_flags & fc4_xpt_flags))
235234
lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM);
@@ -2567,11 +2566,7 @@ lpfc_nvme_rescan_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
25672566
* nvme_transport perspective. Loss of an rport just means IO cannot
25682567
* be sent and recovery is completely up to the initator.
25692568
* For now, the driver just unbinds the DID and port_role so that
2570-
* no further IO can be issued. Changes are planned for later.
2571-
*
2572-
* Notes - the ndlp reference count is not decremented here since
2573-
* since there is no nvme_transport api for devloss. Node ref count
2574-
* is only adjusted in driver unload.
2569+
* no further IO can be issued.
25752570
*/
25762571
void
25772572
lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
@@ -2646,6 +2641,21 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
26462641
"6167 NVME unregister failed %d "
26472642
"port_state x%x\n",
26482643
ret, remoteport->port_state);
2644+
2645+
if (vport->load_flag & FC_UNLOADING) {
2646+
/* Only 1 thread can drop the initial node
2647+
* reference. Check if another thread has set
2648+
* NLP_DROPPED.
2649+
*/
2650+
spin_lock_irq(&ndlp->lock);
2651+
if (!(ndlp->nlp_flag & NLP_DROPPED)) {
2652+
ndlp->nlp_flag |= NLP_DROPPED;
2653+
spin_unlock_irq(&ndlp->lock);
2654+
lpfc_nlp_put(ndlp);
2655+
return;
2656+
}
2657+
spin_unlock_irq(&ndlp->lock);
2658+
}
26492659
}
26502660
}
26512661
return;

0 commit comments

Comments
 (0)