* [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs
@ 2026-04-15 20:54 Yuho Choi
2026-05-05 1:40 ` dbgh9129
2026-05-06 19:18 ` Frank Li
0 siblings, 2 replies; 3+ messages in thread
From: Yuho Choi @ 2026-04-15 20:54 UTC (permalink / raw)
To: Vinicius Costa Gomes, Vinod Koul
Cc: Dave Jiang, Frank Li, dmaengine, linux-kernel, Yuho Choi
The release callbacks for wq, engine, and group devices
(idxd_conf_wq_release, idxd_conf_engine_release,
idxd_conf_group_release) each call kfree() on the enclosing struct.
The setup error paths and cleanup functions also call kfree()
explicitly after put_device(), producing a double free whenever
put_device() drops the reference count to zero and fires the release.
In the setup functions, device_initialize() is called before
device_add(), so the reference count is exactly 1 at the error sites.
put_device() unconditionally fires the release, which frees the struct;
the subsequent explicit kfree() then operates on freed memory.
For idxd_setup_wqs(), the wq release callback also owns opcap_bmap
and wqcfg. The error unwind additionally freed those fields explicitly
before calling put_device(), causing further double frees on both.
Remove the redundant explicit kfree() calls from all setup error paths
and cleanup functions for wq, engine, and group structs, delegating
sole ownership of those allocations to the release callbacks.
Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime")
Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime")
Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
drivers/dma/idxd/init.c | 36 +++++-------------------------------
1 file changed, 5 insertions(+), 31 deletions(-)
diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
index f1cfc7790d950..4b827a3297564 100644
--- a/drivers/dma/idxd/init.c
+++ b/drivers/dma/idxd/init.c
@@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd)
static void idxd_clean_wqs(struct idxd_device *idxd)
{
- struct idxd_wq *wq;
struct device *conf_dev;
int i;
for (i = 0; i < idxd->max_wqs; i++) {
- wq = idxd->wqs[i];
- if (idxd->hw.wq_cap.op_config)
- bitmap_free(wq->opcap_bmap);
- kfree(wq->wqcfg);
- conf_dev = wq_confdev(wq);
+ conf_dev = wq_confdev(idxd->wqs[i]);
put_device(conf_dev);
- kfree(wq);
}
bitmap_free(idxd->wq_enable_map);
kfree(idxd->wqs);
@@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(wq);
goto err_unwind;
}
@@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
if (!wq->wqcfg) {
put_device(conf_dev);
- kfree(wq);
rc = -ENOMEM;
goto err_unwind;
}
@@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
if (idxd->hw.wq_cap.op_config) {
wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
if (!wq->opcap_bmap) {
- kfree(wq->wqcfg);
put_device(conf_dev);
- kfree(wq);
rc = -ENOMEM;
goto err_unwind;
}
@@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
err_unwind:
while (--i >= 0) {
- wq = idxd->wqs[i];
- if (idxd->hw.wq_cap.op_config)
- bitmap_free(wq->opcap_bmap);
- kfree(wq->wqcfg);
- conf_dev = wq_confdev(wq);
+ conf_dev = wq_confdev(idxd->wqs[i]);
put_device(conf_dev);
- kfree(wq);
}
bitmap_free(idxd->wq_enable_map);
@@ -270,15 +255,12 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
static void idxd_clean_engines(struct idxd_device *idxd)
{
- struct idxd_engine *engine;
struct device *conf_dev;
int i;
for (i = 0; i < idxd->max_engines; i++) {
- engine = idxd->engines[i];
- conf_dev = engine_confdev(engine);
+ conf_dev = engine_confdev(idxd->engines[i]);
put_device(conf_dev);
- kfree(engine);
}
kfree(idxd->engines);
}
@@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(engine);
goto err;
}
@@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd)
err:
while (--i >= 0) {
- engine = idxd->engines[i];
- conf_dev = engine_confdev(engine);
+ conf_dev = engine_confdev(idxd->engines[i]);
put_device(conf_dev);
- kfree(engine);
}
kfree(idxd->engines);
@@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd)
static void idxd_clean_groups(struct idxd_device *idxd)
{
- struct idxd_group *group;
int i;
for (i = 0; i < idxd->max_groups; i++) {
- group = idxd->groups[i];
- put_device(group_confdev(group));
- kfree(group);
+ put_device(group_confdev(idxd->groups[i]));
}
kfree(idxd->groups);
}
@@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id);
if (rc < 0) {
put_device(conf_dev);
- kfree(group);
goto err;
}
@@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
while (--i >= 0) {
group = idxd->groups[i];
put_device(group_confdev(group));
- kfree(group);
}
kfree(idxd->groups);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs
2026-04-15 20:54 [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs Yuho Choi
@ 2026-05-05 1:40 ` dbgh9129
2026-05-06 19:18 ` Frank Li
1 sibling, 0 replies; 3+ messages in thread
From: dbgh9129 @ 2026-05-05 1:40 UTC (permalink / raw)
To: vinicius.gomes, vkoul
Cc: dave.jiang, Frank.Li, dmaengine, linux-kernel, dbgh9129
Dear Vinicius and Vinod,
I am sending a gentle ping on this patch. Please let me know if you
have any feedback or if any changes are required.
Best regards,
Yuho Choi
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs
2026-04-15 20:54 [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs Yuho Choi
2026-05-05 1:40 ` dbgh9129
@ 2026-05-06 19:18 ` Frank Li
1 sibling, 0 replies; 3+ messages in thread
From: Frank Li @ 2026-05-06 19:18 UTC (permalink / raw)
To: Yuho Choi
Cc: Vinicius Costa Gomes, Vinod Koul, Dave Jiang, Frank Li, dmaengine,
linux-kernel
On Wed, Apr 15, 2026 at 04:54:52PM -0400, Yuho Choi wrote:
> The release callbacks for wq, engine, and group devices
> (idxd_conf_wq_release, idxd_conf_engine_release,
> idxd_conf_group_release) each call kfree() on the enclosing struct.
> The setup error paths and cleanup functions also call kfree()
> explicitly after put_device(), producing a double free whenever
> put_device() drops the reference count to zero and fires the release.
>
> In the setup functions, device_initialize() is called before
> device_add(), so the reference count is exactly 1 at the error sites.
> put_device() unconditionally fires the release, which frees the struct;
> the subsequent explicit kfree() then operates on freed memory.
>
> For idxd_setup_wqs(), the wq release callback also owns opcap_bmap
> and wqcfg. The error unwind additionally freed those fields explicitly
> before calling put_device(), causing further double frees on both.
>
> Remove the redundant explicit kfree() calls from all setup error paths
> and cleanup functions for wq, engine, and group structs, delegating
> sole ownership of those allocations to the release callbacks.
>
> Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime")
> Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime")
> Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime")
> Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/idxd/init.c | 36 +++++-------------------------------
> 1 file changed, 5 insertions(+), 31 deletions(-)
>
> diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
> index f1cfc7790d950..4b827a3297564 100644
> --- a/drivers/dma/idxd/init.c
> +++ b/drivers/dma/idxd/init.c
> @@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd)
>
> static void idxd_clean_wqs(struct idxd_device *idxd)
> {
> - struct idxd_wq *wq;
> struct device *conf_dev;
> int i;
>
> for (i = 0; i < idxd->max_wqs; i++) {
> - wq = idxd->wqs[i];
> - if (idxd->hw.wq_cap.op_config)
> - bitmap_free(wq->opcap_bmap);
> - kfree(wq->wqcfg);
> - conf_dev = wq_confdev(wq);
> + conf_dev = wq_confdev(idxd->wqs[i]);
> put_device(conf_dev);
> - kfree(wq);
> }
> bitmap_free(idxd->wq_enable_map);
> kfree(idxd->wqs);
> @@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
> rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id);
> if (rc < 0) {
> put_device(conf_dev);
> - kfree(wq);
> goto err_unwind;
> }
>
> @@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
> wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev));
> if (!wq->wqcfg) {
> put_device(conf_dev);
> - kfree(wq);
> rc = -ENOMEM;
> goto err_unwind;
> }
> @@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
> if (idxd->hw.wq_cap.op_config) {
> wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL);
> if (!wq->opcap_bmap) {
> - kfree(wq->wqcfg);
> put_device(conf_dev);
> - kfree(wq);
> rc = -ENOMEM;
> goto err_unwind;
> }
> @@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
>
> err_unwind:
> while (--i >= 0) {
> - wq = idxd->wqs[i];
> - if (idxd->hw.wq_cap.op_config)
> - bitmap_free(wq->opcap_bmap);
> - kfree(wq->wqcfg);
> - conf_dev = wq_confdev(wq);
> + conf_dev = wq_confdev(idxd->wqs[i]);
> put_device(conf_dev);
> - kfree(wq);
> }
> bitmap_free(idxd->wq_enable_map);
>
> @@ -270,15 +255,12 @@ static int idxd_setup_wqs(struct idxd_device *idxd)
>
> static void idxd_clean_engines(struct idxd_device *idxd)
> {
> - struct idxd_engine *engine;
> struct device *conf_dev;
> int i;
>
> for (i = 0; i < idxd->max_engines; i++) {
> - engine = idxd->engines[i];
> - conf_dev = engine_confdev(engine);
> + conf_dev = engine_confdev(idxd->engines[i]);
> put_device(conf_dev);
> - kfree(engine);
> }
> kfree(idxd->engines);
> }
> @@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd)
> rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id);
> if (rc < 0) {
> put_device(conf_dev);
> - kfree(engine);
> goto err;
> }
>
> @@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd)
>
> err:
> while (--i >= 0) {
> - engine = idxd->engines[i];
> - conf_dev = engine_confdev(engine);
> + conf_dev = engine_confdev(idxd->engines[i]);
> put_device(conf_dev);
> - kfree(engine);
> }
> kfree(idxd->engines);
>
> @@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd)
>
> static void idxd_clean_groups(struct idxd_device *idxd)
> {
> - struct idxd_group *group;
> int i;
>
> for (i = 0; i < idxd->max_groups; i++) {
> - group = idxd->groups[i];
> - put_device(group_confdev(group));
> - kfree(group);
> + put_device(group_confdev(idxd->groups[i]));
> }
> kfree(idxd->groups);
> }
> @@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
> rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id);
> if (rc < 0) {
> put_device(conf_dev);
> - kfree(group);
> goto err;
> }
>
> @@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd)
> while (--i >= 0) {
> group = idxd->groups[i];
> put_device(group_confdev(group));
> - kfree(group);
> }
> kfree(idxd->groups);
>
> --
> 2.50.1 (Apple Git-155)
>
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-06 19:18 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-15 20:54 [PATCH v1] dmaengine: idxd: fix double free of wq, engine, and group structs Yuho Choi
2026-05-05 1:40 ` dbgh9129
2026-05-06 19:18 ` Frank Li
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox