scsi: lpfc: nvmet: avoid hang / use-after-free when destroying targetport
authorEwan D. Milne <emilne@redhat.com>
Thu, 17 Jan 2019 16:14:45 +0000 (11:14 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 5 Mar 2019 16:58:50 +0000 (17:58 +0100)
[ Upstream commit c41f59884be5cca293ed61f3d64637dbba3a6381 ]

We cannot wait on a completion object in the lpfc_nvme_targetport structure
in the _destroy_targetport() code path because the NVMe/fc transport will
free that structure immediately after the .targetport_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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/scsi/lpfc/lpfc_nvmet.c
drivers/scsi/lpfc/lpfc_nvmet.h

index b766afe..e2575c8 100644 (file)
@@ -1003,7 +1003,8 @@ lpfc_nvmet_targetport_delete(struct nvmet_fc_target_port *targetport)
        struct lpfc_nvmet_tgtport *tport = targetport->private;
 
        /* release any threads waiting for the unreg to complete */
-       complete(&tport->tport_unreg_done);
+       if (tport->phba->targetport)
+               complete(tport->tport_unreg_cmp);
 }
 
 static void
@@ -1700,6 +1701,7 @@ lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
        struct lpfc_nvmet_tgtport *tgtp;
        struct lpfc_queue *wq;
        uint32_t qidx;
+       DECLARE_COMPLETION_ONSTACK(tport_unreg_cmp);
 
        if (phba->nvmet_support == 0)
                return;
@@ -1709,9 +1711,9 @@ lpfc_nvmet_destroy_targetport(struct lpfc_hba *phba)
                        wq = phba->sli4_hba.nvme_wq[qidx];
                        lpfc_nvmet_wqfull_flush(phba, wq, NULL);
                }
-               init_completion(&tgtp->tport_unreg_done);
+               tgtp->tport_unreg_cmp = &tport_unreg_cmp;
                nvmet_fc_unregister_targetport(phba->targetport);
-               wait_for_completion_timeout(&tgtp->tport_unreg_done, 5);
+               wait_for_completion_timeout(&tport_unreg_cmp, 5);
                lpfc_nvmet_cleanup_io_context(phba);
        }
        phba->targetport = NULL;
index 1aaff63..0ec1082 100644 (file)
@@ -34,7 +34,7 @@
 /* Used for NVME Target */
 struct lpfc_nvmet_tgtport {
        struct lpfc_hba *phba;
-       struct completion tport_unreg_done;
+       struct completion *tport_unreg_cmp;
 
        /* Stats counters - lpfc_nvmet_unsol_ls_buffer */
        atomic_t rcv_ls_req_in;