mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-29 23:53:55 +08:00
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: Ewan D. Milne <emilne@redhat.com> Acked-by: James Smart <james.smart@broadcom.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
a8cf59a669
commit
7961cba6f7
@ -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
|
||||
@ -2545,7 +2546,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;
|
||||
@ -2557,8 +2559,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 "
|
||||
@ -2592,12 +2593,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;
|
||||
|
||||
@ -2608,13 +2609,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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user