Message ID | 20170329111754.GA9183@linux-x5ow.site (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
On 03/29/17 20:17, Johannes Thumshirn wrote: > On Wed, Mar 29, 2017 at 02:29:45AM +0000, Junichi Nomura wrote: >> The double-free occurs as followings: >> - During initialization, lpfc_create_wq_cq() binds cq and wq to >> the same ring in the way that both cq->pring and wq->pring point >> to the same object. >> - Upon removal, lpfc_sli4_queue_destroy() ends up calling >> lpfc_sli4_queue_free() for both wqs and cqs >> and kfree(queue->pring) is done twice. >> >> The problem became more visible in v4.11-rc3 because commit 85e8a23936ab >> ("scsi: lpfc: Add shutdown method for kexec") made lpfc_pci_remove_one() >> called during driver shutdown. > > Well the obvious band-aid would be setting the pointers to NULL after freeing > them. lpfc_sli4_queue_free() checks for queue's precense and doesn't use > queue->pring prior to freeing it, so the following _should_ to the trick: > > From befa936d8935a1bed01df65b376f515fa42c99da Mon Sep 17 00:00:00 2001 > From: Johannes Thumshirn <jthumshirn@suse.de> > Date: Wed, 29 Mar 2017 13:08:55 +0200 > Subject: [PATCH] lpfc: prevent double free of lpfc queue ring pointer > > Since commit 895427bd012c ("scsi: lpfc: NVME Initiator: Base modifications") > rmoving the lpfc module causes a double free in lpfc_sli4_queue_free(). > > This can be prevented by setting the queue->pring and queue pointers to NULL, > so kfree() will simply ignore the pointers on a second call. No, it doesn't work. Even if lpfc_sli4_queue_free(wq) sets wq->pring to NULL, cq->pring still holds bogus pointer and lpfc_sli4_queue_free(cq) will call kfree(cq->pring) and cause double-free.
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 1c9fa45..86e1529 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -13759,7 +13759,9 @@ lpfc_sli4_queue_free(struct lpfc_queue *queue) kfree(queue->rqbp); } kfree(queue->pring); + queue->pring = NULL; kfree(queue); + queue = NULL; return; }