[PATCH] scsi: qedf: Fix memory leak in __qedf_probe()
From: Dawei Feng
Date: Tue Jun 16 2026 - 10:03:59 EST
qedf_set_fcoe_pf_param() allocates PF queue state for the firmware FCoE
parameters. If qedf_alloc_global_queues() fails after partial allocation,
the existing unwind leaves p_cpuq and global_queues allocated. If later
__qedf_probe() steps fail after PF parameter setup succeeds, the same PF
queue state is also left behind.
Route those paths through qedf_free_fcoe_pf_param() and clear the freed
PF queue pointers so the helper-internal and probe-level unwinds can share
the same cleanup safely.
The bug was first flagged by an experimental analysis tool we are
developing for kernel memory-management bugs while analyzing
v6.13-rc1. The tool is still under development and is not yet publicly
available. Manual inspection confirms that the bug is still present in
v7.1-rc7.
An x86_64 allyesconfig build showed no new warnings. As we do not have a
QLogic FastLinQ FCoE adapter to test with, no runtime testing was able to
be performed.
Fixes: 61d8658b4a43 ("scsi: qedf: Add QLogic FastLinQ offload FCoE driver framework.")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Dawei Feng <dawei.feng@xxxxxxxxxx>
---
drivers/scsi/qedf/qedf_main.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c
index da429b3a4283..3a41c0210336 100644
--- a/drivers/scsi/qedf/qedf_main.c
+++ b/drivers/scsi/qedf/qedf_main.c
@@ -32,6 +32,7 @@ static void qedf_shutdown(struct pci_dev *pdev);
static void qedf_schedule_recovery_handler(void *dev);
static void qedf_recovery_handler(struct work_struct *work);
static int qedf_suspend(struct pci_dev *pdev, pm_message_t state);
+static void qedf_free_fcoe_pf_param(struct qedf_ctx *qedf);
/*
* Driver module parameters.
@@ -2927,15 +2928,18 @@ static void qedf_free_bdq(struct qedf_ctx *qedf)
if (qedf->bdq_pbl_list)
dma_free_coherent(&qedf->pdev->dev, QEDF_PAGE_SIZE,
qedf->bdq_pbl_list, qedf->bdq_pbl_list_dma);
+ qedf->bdq_pbl_list = NULL;
if (qedf->bdq_pbl)
dma_free_coherent(&qedf->pdev->dev, qedf->bdq_pbl_mem_size,
qedf->bdq_pbl, qedf->bdq_pbl_dma);
+ qedf->bdq_pbl = NULL;
for (i = 0; i < QEDF_BDQ_SIZE; i++) {
if (qedf->bdq[i].buf_addr) {
dma_free_coherent(&qedf->pdev->dev, QEDF_BDQ_BUF_SIZE,
qedf->bdq[i].buf_addr, qedf->bdq[i].buf_dma);
+ qedf->bdq[i].buf_addr = NULL;
}
}
}
@@ -2945,6 +2949,9 @@ static void qedf_free_global_queues(struct qedf_ctx *qedf)
int i;
struct global_queue **gl = qedf->global_queues;
+ if (!gl)
+ return;
+
for (i = 0; i < qedf->num_queues; i++) {
if (!gl[i])
continue;
@@ -2957,6 +2964,7 @@ static void qedf_free_global_queues(struct qedf_ctx *qedf)
gl[i]->cq_pbl, gl[i]->cq_pbl_dma);
kfree(gl[i]);
+ gl[i] = NULL;
}
qedf_free_bdq(qedf);
@@ -3201,6 +3209,7 @@ static int qedf_set_fcoe_pf_param(struct qedf_ctx *qedf)
if (rval) {
QEDF_ERR(&(qedf->dbg_ctx), "Global queue allocation "
"failed.\n");
+ qedf_free_fcoe_pf_param(qedf);
return 1;
}
@@ -3264,11 +3273,14 @@ static void qedf_free_fcoe_pf_param(struct qedf_ctx *qedf)
size = qedf->num_queues * sizeof(struct qedf_glbl_q_params);
dma_free_coherent(&qedf->pdev->dev, size, qedf->p_cpuq,
qedf->hw_p_cpuq);
+ qedf->p_cpuq = NULL;
+ qedf->hw_p_cpuq = 0;
}
qedf_free_global_queues(qedf);
kfree(qedf->global_queues);
+ qedf->global_queues = NULL;
}
/*
@@ -3443,7 +3455,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info);
if (rc) {
QEDF_ERR(&qedf->dbg_ctx, "Failed to fill dev info.\n");
- goto err2;
+ goto err2_free_pf;
}
if (mode != QEDF_MODE_RECOVERY) {
@@ -3452,7 +3464,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
QEDF_ERR(&qedf->dbg_ctx, "Cannot register devlink\n");
rc = PTR_ERR(qedf->devlink);
qedf->devlink = NULL;
- goto err2;
+ goto err2_free_pf;
}
}
@@ -3469,7 +3481,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
if (rc) {
QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
- goto err2;
+ goto err2_free_pf;
}
/* Start the Slowpath-process */
@@ -3483,7 +3495,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
rc = qed_ops->common->slowpath_start(qedf->cdev, &slowpath_params);
if (rc) {
QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
- goto err2;
+ goto err2_free_pf;
}
/*
@@ -3713,10 +3725,11 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
err5:
qed_ops->stop(qedf->cdev);
err4:
- qedf_free_fcoe_pf_param(qedf);
qedf_sync_free_irqs(qedf);
err3:
qed_ops->common->slowpath_stop(qedf->cdev);
+err2_free_pf:
+ qedf_free_fcoe_pf_param(qedf);
err2:
qed_ops->common->remove(qedf->cdev);
err1:
--
2.34.1