All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ziyou Wang (Lenovo)" <ziyou.dev@gmail.com>
Cc: dmaengine@vger.kernel.org, vkoul@kernel.org, Frank.Li@kernel.org
Subject: Re: [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove
Date: Fri, 11 Sep 2026 07:14:12 +0000	[thread overview]
Message-ID: <20260911071413.4096C1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260911065547.161522-1-ziyou.dev@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

Pre-existing issues:
- [High] Pre-existing Use-After-Free and Double Free in the error path of `idxd_register_devices()` and `idxd_pci_probe_alloc()`.
- [High] Pre-existing double free and UAF of the IDXD device and its resources in `idxd_free()`.
- [High] Flawed `while (j--)` loop condition in device registration cleanup paths causes an out-of-bounds array read on the first iteration failure, and skips unregistering elements on subsequent iteration failures.
- [Medium] Pre-existing memory leak of the `saved_engines` array in the error paths of `idxd_device_config_save`.
--

commit cf8581894d15c4c8e08a44d0ae7b7e0a1a4eb54c
Author: Ziyou Wang (Lenovo) <ziyou.dev@gmail.com>

dmaengine: idxd: Fix use-after-free in idxd_remove

This commit fixes a use-after-free bug in the idxd driver unbind path
by taking reference counts on the wq, engine, and group devices before
unregistering them. It adds idxd_put_devices() to release these references
after all cleanup operations are complete.

> diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
> index 4b827a3297564..212efbfebacce 100644
> --- a/drivers/dma/idxd/init.c
> +++ b/drivers/dma/idxd/init.c

[ ... ]

[Severity: Medium]
This is a pre-existing issue, but does idxd_device_config_save() leak the
saved_engines array on error?

Looking at idxd_device_config_save() in drivers/dma/idxd/init.c:

	struct idxd_engine **saved_engines =
			kcalloc_node(idxd->max_engines,
				     sizeof(struct idxd_engine *),
				     GFP_KERNEL, dev_to_node(dev));

The saved_engines array is allocated without the __free(kfree) cleanup
attribute. If a memory allocation fails later in the function (for example,
when the device undergoes a reset and memory allocation fails), it jumps to
an error path that returns -ENOMEM.

The idxd_free_saved() function frees the individual structures via
idxd_free_saved_configs(), but assumes the arrays themselves are freed by the
__free cleanup mechanism. Since saved_engines lacks this attribute, is its
memory permanently leaked here?


[Severity: High]
This is a pre-existing issue, but can idxd_free() trigger a double-free
and use-after-free?

Looking at idxd_free() in drivers/dma/idxd/init.c:

static void idxd_free(struct idxd_device *idxd)
{
	if (!idxd)
		return;

	put_device(idxd_confdev(idxd));
	bitmap_free(idxd->opcap_bmap);
	ida_free(&idxd_ida, idxd->id);
	kfree(idxd);
}

When idxd_free() is called on early probe error paths, it first calls
put_device(idxd_confdev(idxd)). This drops the device refcount to 0 and
triggers the idxd_conf_device_release() callback, which explicitly frees
internal structures, calls bitmap_free(idxd->opcap_bmap),
ida_free(&idxd_ida, idxd->id), and then kfree(idxd).

After put_device() returns, idxd_free() immediately repeats these exact
same cleanup steps on the now-freed idxd structure. Does this result in a
direct use-after-free and double-free?

> diff --git a/drivers/dma/idxd/sysfs.c b/drivers/dma/idxd/sysfs.c
> index 6d251095c350f..eca96a4dcecd8 100644
> --- a/drivers/dma/idxd/sysfs.c
> +++ b/drivers/dma/idxd/sysfs.c

[ ... ]

[Severity: High]
This is a pre-existing issue, but does idxd_register_devices() have a similar
use-after-free and double-free pattern in its error path?

Looking at idxd_register_devices() in drivers/dma/idxd/sysfs.c:

 err_group:
	for (i = 0; i < idxd->max_engines; i++)
		device_unregister(engine_confdev(idxd->engines[i]));
 err_engine:
	for (i = 0; i < idxd->max_wqs; i++)
		device_unregister(wq_confdev(idxd->wqs[i]));
 err_wq:
	device_del(idxd_confdev(idxd));

If device_add() fails in idxd_register_wq_devices() or subsequent steps,
this cleanup path calls device_unregister() on previously added engines and
WQs, freeing them. It then calls device_del(idxd_confdev(idxd)), which
unbinds the driver. This invokes idxd_device_drv_remove():

drivers/dma/idxd/device.c:idxd_device_drv_remove() {
	...
	for (i = 0; i < idxd->max_wqs; i++) {
		struct idxd_wq *wq = idxd->wqs[i];
		struct device *wq_dev = wq_confdev(wq);

		if (wq->state == IDXD_WQ_DISABLED)
			continue;
	...
}

This loop blindly accesses the WQ states which have already been freed by
the sysfs registration error handler, causing a direct use-after-free.

Furthermore, after idxd_register_devices() returns an error,
idxd_pci_probe_alloc() in drivers/dma/idxd/init.c jumps to err_dev_register:

drivers/dma/idxd/init.c:idxd_pci_probe_alloc() {
	...
 err_dev_register:
	idxd_cleanup(idxd);
	...
}

Which eventually calls idxd_clean_wqs():

drivers/dma/idxd/init.c:idxd_clean_wqs() {
	...
	for (i = 0; i < idxd->max_wqs; i++) {
		conf_dev = wq_confdev(idxd->wqs[i]);
		put_device(conf_dev);
	}
	...
}

This blindly drops references on objects that were already freed in the
idxd_register_devices() rollback, triggering a double free.


[Severity: High]
This is a pre-existing issue, but do the registration error paths have an
out-of-bounds array read?

Looking at idxd_register_wq_devices() in drivers/dma/idxd/sysfs.c:

	while (j--) {
		wq = idxd->wqs[j];
		device_unregister(wq_confdev(wq));
	}

In idxd_register_wq_devices(), idxd_register_engine_devices(), and
idxd_register_group_devices(), the cleanup paths use j = i - 1 followed by
while (j--).

If device_add() fails on the very first element (i = 0), j becomes -1. The
while (j--) condition evaluates the current value -1 as true (non-zero) and
decrements j to -2. The loop body then accesses index -2 (for example,
idxd->wqs[-2]) and passes the out-of-bounds pointer to device_unregister().

Conversely, if it fails on the second element (i = 1), j becomes 0. The
loop evaluates 0 as false and never executes. Does this skip unregistering
and leak the successfully added element at index 0?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260911065547.161522-1-ziyou.dev@gmail.com?part=1

      reply	other threads:[~2026-09-11  7:14 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-11  6:55 [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove Ziyou Wang (Lenovo)
2026-09-11  7:14 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260911071413.4096C1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vkoul@kernel.org \
    --cc=ziyou.dev@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.