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
reply other threads:[~2026-09-11 6:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®