Commit 7961cba6 authored by Ewan D. Milne's avatar Ewan D. Milne Committed by Martin K. Petersen

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

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>
parent a8cf59a6
...@@ -297,7 +297,8 @@ lpfc_nvme_localport_delete(struct nvme_fc_local_port *localport) ...@@ -297,7 +297,8 @@ lpfc_nvme_localport_delete(struct nvme_fc_local_port *localport)
lport); lport);
/* release any threads waiting for the unreg to complete */ /* 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 /* lpfc_nvme_remoteport_delete
...@@ -2545,7 +2546,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport) ...@@ -2545,7 +2546,8 @@ lpfc_nvme_create_localport(struct lpfc_vport *vport)
*/ */
void void
lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, 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)) #if (IS_ENABLED(CONFIG_NVME_FC))
u32 wait_tmo; u32 wait_tmo;
...@@ -2557,8 +2559,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport, ...@@ -2557,8 +2559,7 @@ lpfc_nvme_lport_unreg_wait(struct lpfc_vport *vport,
*/ */
wait_tmo = msecs_to_jiffies(LPFC_NVME_WAIT_TMO * 1000); wait_tmo = msecs_to_jiffies(LPFC_NVME_WAIT_TMO * 1000);
while (true) { while (true) {
ret = wait_for_completion_timeout(&lport->lport_unreg_done, ret = wait_for_completion_timeout(lport_unreg_cmp, wait_tmo);
wait_tmo);
if (unlikely(!ret)) { if (unlikely(!ret)) {
lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR, lpfc_printf_vlog(vport, KERN_ERR, LOG_NVME_IOERR,
"6176 Lport %p Localport %p wait " "6176 Lport %p Localport %p wait "
...@@ -2592,12 +2593,12 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) ...@@ -2592,12 +2593,12 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
struct lpfc_nvme_lport *lport; struct lpfc_nvme_lport *lport;
struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_nvme_ctrl_stat *cstat;
int ret; int ret;
DECLARE_COMPLETION_ONSTACK(lport_unreg_cmp);
if (vport->nvmei_support == 0) if (vport->nvmei_support == 0)
return; return;
localport = vport->localport; localport = vport->localport;
vport->localport = NULL;
lport = (struct lpfc_nvme_lport *)localport->private; lport = (struct lpfc_nvme_lport *)localport->private;
cstat = lport->cstat; cstat = lport->cstat;
...@@ -2608,13 +2609,14 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport) ...@@ -2608,13 +2609,14 @@ lpfc_nvme_destroy_localport(struct lpfc_vport *vport)
/* lport's rport list is clear. Unregister /* lport's rport list is clear. Unregister
* lport and release resources. * lport and release resources.
*/ */
init_completion(&lport->lport_unreg_done); lport->lport_unreg_cmp = &lport_unreg_cmp;
ret = nvme_fc_unregister_localport(localport); ret = nvme_fc_unregister_localport(localport);
/* Wait for completion. This either blocks /* Wait for completion. This either blocks
* indefinitely or succeeds * 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); kfree(cstat);
/* Regardless of the unregister upcall response, clear /* Regardless of the unregister upcall response, clear
......
...@@ -50,7 +50,7 @@ struct lpfc_nvme_ctrl_stat { ...@@ -50,7 +50,7 @@ struct lpfc_nvme_ctrl_stat {
/* Declare nvme-based local and remote port definitions. */ /* Declare nvme-based local and remote port definitions. */
struct lpfc_nvme_lport { struct lpfc_nvme_lport {
struct lpfc_vport *vport; struct lpfc_vport *vport;
struct completion lport_unreg_done; struct completion *lport_unreg_cmp;
/* Add stats counters here */ /* Add stats counters here */
struct lpfc_nvme_ctrl_stat *cstat; struct lpfc_nvme_ctrl_stat *cstat;
atomic_t fc4NvmeLsRequests; atomic_t fc4NvmeLsRequests;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment