From: "Ziyou Wang (Lenovo)" <ziyou.dev@gmail.com>
To: Vinod Koul <vkoul@kernel.org>,
Vinicius Costa Gomes <vinicius.gomes@intel.com>
Cc: Dave Jiang <dave.jiang@intel.com>, Frank Li <Frank.Li@kernel.org>,
dmaengine@vger.kernel.org, linux-kernel@vger.kernel.org,
"Ziyou Wang (Lenovo)" <ziyou.dev@gmail.com>,
Adrian Huang <adrianhuang0701@gmail.com>
Subject: [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove
Date: Fri, 11 Sep 2026 14:55:47 +0800 [thread overview]
Message-ID: <20260911065547.161522-1-ziyou.dev@gmail.com> (raw)
When unbinding the idxd driver, the following call trace is observed:
general protection fault, probably for non-canonical address 0x18fc1d6937256657
CPU: 117 UID: 0 PID: 3396 Comm: python3 Tainted: G E 7.2.0-rc5-latest+ #11 PREEMPT(lazy)
Hardware name: Lenovo WenTian WR5220 G5, BIOS speb50m-2.54
RIP: 0010:__refill_objects_node+0x2da/0x5e0
Call Trace:
<TASK>
refill_objects+0x1ee/0x2e0
__pcs_replace_empty_main+0x1e1/0x330
__kmalloc_noprof+0x503/0x550
ext4_htree_store_dirent+0x34/0x110 [ext4]
htree_dirblock_to_tree+0x1a9/0x2c0 [ext4]
ext4_htree_fill_tree+0x246/0x3d0 [ext4]
ext4_readdir+0x863/0x9d0 [ext4]
iterate_dir+0xa6/0x260
__x64_sys_getdents64+0x76/0x130
do_syscall_64+0x98/0x620
entry_SYSCALL_64_after_hwframe+0x76/0x7e
</TASK>
With the KASAN-enabled kernel, the kernel reports a use-after-free bug:
idxd_unregister_devices() calls device_unregister(wq), which triggers
the wq release callback and frees wq memory via kfree(). However,
idxd->wqs[] array still holds the freed pointer. Later, when
device_unregister(idxd) calls idxd_device_drv_remove(), it accesses
wq->state through idxd->wqs[], resulting in use-after-free.
The same issue exists for idxd->engines[] and idxd->groups[] arrays.
KASAN report:
BUG: KASAN: slab-use-after-free in idxd_device_drv_remove+0xc1/0x100 [idxd]
Read of size 4 at addr ff1100038d296414 by task python3/3354
CPU: 20 UID: 0 PID: 3354 Comm: python3 Tainted: G E 7.2.0-rc5-debug+
Hardware name: Lenovo WenTian WR5220 G5, BIOS speb50m-2.54
Call Trace:
<TASK>
dump_stack_lvl+0x5b/0x80
print_report+0x153/0x4b5
kasan_report+0xbc/0xf0
idxd_device_drv_remove+0xc1/0x100 [idxd]
device_release_driver_internal+0x244/0x2e0
bus_remove_device+0x195/0x2b0
device_del+0x24b/0x540
device_unregister+0x17/0x80
idxd_remove+0x3f/0x120 [idxd]
pci_device_remove+0x6d/0xf0
device_release_driver_internal+0x244/0x2e0
unbind_store+0xae/0xb0
kernfs_fop_write_iter+0x205/0x2d0
vfs_write+0x3a9/0x6b0
ksys_write+0xc7/0x160
do_syscall_64+0x9c/0x620
entry_SYSCALL_64_after_hwframe+0x76/0x7e
</TASK>
Allocated by task 1219:
kasan_save_stack+0x20/0x40
kasan_save_track+0x14/0x30
__kasan_kmalloc+0x9a/0xb0
__kmalloc_cache_node_noprof+0x1c6/0x470
idxd_pci_probe_alloc+0x103f/0x2430 [idxd]
local_pci_probe+0x71/0xd0
local_pci_probe_callback+0x20/0x40
process_one_work+0x389/0x6a0
worker_thread+0x321+0x590
kthread+0x1b4/0x200
ret_from_fork+0x3bd/0x4d0
ret_from_fork_asm+0x1a/0x30
Freed by task 3354:
kasan_save_stack+0x20/0x40
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x70
__kasan_slab_free+0x6b/0x90
kfree+0x1d4/0x520
device_release+0x77/0x120
kobject_put+0xdb/0x2a0
idxd_unregister_devices+0x5b/0x130 [idxd]
idxd_remove+0x2f/0x120 [idxd]
pci_device_remove+0x6d/0xf0
device_release_driver_internal+0x244/0x2e0
unbind_store+0xae/0xb0
kernfs_fop_write_iter+0x205/0x2d0
vfs_write+0x3a9/0x6b0
ksys_write+0xc7/0x160
do_syscall_64+0x9c/0x620
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fix by taking refs on wq/engine/group conf_dev in idxd_unregister_devices()
before calling device_unregister(). This prevents their memory from being
freed until after idxd_device_drv_remove() completes. Add idxd_put_devices()
to release the refs after all cleanup activities are done.
To reproduce:
cd dsa-perf-micros
./scripts/setup_dsa.sh -d dsa0 -w 1 -m s -e 4
echo "0000:00:01.0" > /sys/bus/pci/drivers/idxd/unbind
Fixes: 47c16ac27d4c ("dmaengine: idxd: fix idxd conf_dev 'struct device' lifetime")
Suggested-by: Adrian Huang (Lenovo) <adrianhuang0701@gmail.com>
Signed-off-by: Ziyou Wang (Lenovo) <ziyou.dev@gmail.com>
---
Added a Suggested-by tag for Adrian, as he came up with the idea for this patch.
drivers/dma/idxd/init.c | 15 +++++++++++++++
drivers/dma/idxd/sysfs.c | 8 ++++++++
2 files changed, 23 insertions(+)
diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c
index 4b827a329756..212efbfebacc 100644
--- a/drivers/dma/idxd/init.c
+++ b/drivers/dma/idxd/init.c
@@ -1263,6 +1263,20 @@ static void idxd_shutdown(struct pci_dev *pdev)
flush_workqueue(idxd->wq);
}
+static void idxd_put_devices(struct idxd_device *idxd)
+{
+ int i;
+
+ for (i = 0; i < idxd->max_wqs; i++)
+ put_device(wq_confdev(idxd->wqs[i]));
+
+ for (i = 0; i < idxd->max_engines; i++)
+ put_device(engine_confdev(idxd->engines[i]));
+
+ for (i = 0; i < idxd->max_groups; i++)
+ put_device(group_confdev(idxd->groups[i]));
+}
+
static void idxd_remove(struct pci_dev *pdev)
{
struct idxd_device *idxd = pci_get_drvdata(pdev);
@@ -1284,6 +1298,7 @@ static void idxd_remove(struct pci_dev *pdev)
if (device_pasid_enabled(idxd))
idxd_disable_system_pasid(idxd);
pci_iounmap(pdev, idxd->reg_base);
+ idxd_put_devices(idxd);
put_device(idxd_confdev(idxd));
pci_disable_device(pdev);
}
diff --git a/drivers/dma/idxd/sysfs.c b/drivers/dma/idxd/sysfs.c
index 6d251095c350..eca96a4dcecd 100644
--- a/drivers/dma/idxd/sysfs.c
+++ b/drivers/dma/idxd/sysfs.c
@@ -1988,21 +1988,29 @@ void idxd_unregister_devices(struct idxd_device *idxd)
{
int i;
+ /*
+ * Take a reference count to the idxd device to prevent the
+ * corresponding idxd context from being freed, as the driver
+ * still needs it for the remaining cleanup operations.
+ */
for (i = 0; i < idxd->max_wqs; i++) {
struct idxd_wq *wq = idxd->wqs[i];
+ get_device(wq_confdev(wq));
device_unregister(wq_confdev(wq));
}
for (i = 0; i < idxd->max_engines; i++) {
struct idxd_engine *engine = idxd->engines[i];
+ get_device(engine_confdev(engine));
device_unregister(engine_confdev(engine));
}
for (i = 0; i < idxd->max_groups; i++) {
struct idxd_group *group = idxd->groups[i];
+ get_device(group_confdev(group));
device_unregister(group_confdev(group));
}
}
--
2.53.0
next reply other threads:[~2026-09-11 6:56 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 6:55 Ziyou Wang (Lenovo) [this message]
2026-09-11 7:14 ` [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove sashiko-bot
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=20260911065547.161522-1-ziyou.dev@gmail.com \
--to=ziyou.dev@gmail.com \
--cc=Frank.Li@kernel.org \
--cc=adrianhuang0701@gmail.com \
--cc=dave.jiang@intel.com \
--cc=dmaengine@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=vinicius.gomes@intel.com \
--cc=vkoul@kernel.org \
/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.