nvmebuf NULL ptr check is already performed earlier in this routine.
Maybe this patch should rather look like:
diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index fba2e62027b7..cd527324eae7 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -1264,10 +1264,10 @@ lpfc_nvmet_defer_rcv(struct nvmet_fc_target_port *tgtport, atomic_inc(&tgtp->rcv_fcp_cmd_defer);
/* Free the nvmebuf since a new buffer already replaced it */
nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf); spin_lock_irqsave(&ctxp->ctxlock, iflag); ctxp->rqb_buffer = NULL; spin_unlock_irqrestore(&ctxp->ctxlock, iflag);
nvmebuf->hrq->rqbp->rqb_free_buffer(phba, nvmebuf);
}
Regards, Justin Tee
Hi Justin,
Moving free() after clearing is necessary but not sufficient.
The race is between the load of ctxp->rqb_buffer and the clear under ctxp->ctxlock. If we load nvmebuf without the lock, another path can free+NULL it before we take the lock:
T1 (defer_rcv): nvmebuf = ctxp->rqb_buffer; // no lock T2 (ctxbuf_post): [lock] nvmebuf' = ctxp->rqb_buffer; ctxp->rqb_buffer = NULL; free(nvmebuf'); [unlock] T1 (defer_rcv): [lock] ctxp->rqb_buffer = NULL; [unlock]; free(nvmebuf); // UAF/double free