Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30b62656 authored by Ewan D. Milne's avatar Ewan D. Milne Committed by Greg Kroah-Hartman
Browse files

scsi: lpfc: nvme: avoid hang / use-after-free when destroying localport



[ Upstream commit 7961cba6f7d8215fa632df3d220e5154bb825249 ]

We cannot wait on a completion object in the lpfc_nvme_lport structure in
the _destroy_localport() code path because the NVMe/fc transport will free
that structure immediately after the .localport_delete() callback.  This
results in a use-after-free, and a hang if slub_debug=FZPU is enabled.

Fix this by putting the completion on the stack.

Signed-off-by: default avatarEwan D. Milne <emilne@redhat.com>
Acked-by: default avatarJames Smart <james.smart@broadcom.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent edca54b8
Loading
Loading
Loading
Loading
+9 −7
Original line number Diff line number Diff line
@@ -297,7 +297,8 @@ lpfc_nvme_localport_delete(struct nvme_fc_local_port *localport)
			 lport);

	/* release any threads waiting for the unreg to complete */
	complete(&lport->lport_unreg_done);
	if (lport->vport->localport)
		complete(lport->lport_unreg_cmp);
}

/* lpfc_nvme_remoteport_delete
@@ -2556,7 +2557,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
 */
void
lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport,
			   struct lpfc_nvme_lport *lport)
			   struct lpfc_nvme_lport *lport,
			   struct completion *lport_unreg_cmp)
{
#if (IS_ENABLED(CONFIG_NVME_FC))
	u32 wait_tmo;
@@ -2568,8 +2570,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport,
	 */
	wait_tmo = msecs_to_jiffies(LPFC_NVME_WAIT_TMO * 1000);
	while (true) {
		ret = wait_for_completion_timeout(&lport->lport_unreg_done,
						  wait_tmo);
		ret = wait_for_completion_timeout(lport_unreg_cmp, wait_tmo);
		if (unlikely(!ret)) {
			lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR,
					 "6176 Lport %p Localport %p wait "
@@ -2603,12 +2604,12 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
	struct lpfc_nvme_lport *lport;
	struct lpfc_nvme_ctrl_stat *cstat;
	int ret;
	DECLARE_COMPLETION_ONSTACK(lport_unreg_cmp);

	if (vport->nvmei_support == 0)
		return;

	localport = vport->localport;
	vport->localport = NULL;
	lport = (struct lpfc_nvme_lport *)localport->private;
	cstat = lport->cstat;

@@ -2619,13 +2620,14 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
	/* lport's rport list is clear.  Unregister
	 * lport and release resources.
	 */
	init_completion(&lport->lport_unreg_done);
	lport->lport_unreg_cmp = &lport_unreg_cmp;
	ret = nvme_fc_unregister_localport(localport);

	/* Wait for completion.  This either blocks
	 * indefinitely or succeeds
	 */
	lpfc_nvme_lport_unreg_wait(vport, lport);
	lpfc_nvme_lport_unreg_wait(vport, lport, &lport_unreg_cmp);
	vport->localport = NULL;
	kfree(cstat);

	/* Regardless of the unregister upcall response, clear
+1 −1
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ struct lpfc_nvme_ctrl_stat {
/* Declare nvme-based local and remote port definitions. */
struct lpfc_nvme_lport {
	struct lpfc_vport *vport;
	struct completion lport_unreg_done;
	struct completion *lport_unreg_cmp;
	/* Add stats counters here */
	struct lpfc_nvme_ctrl_stat *cstat;
	atomic_t fc4NvmeLsRequests;