All of lore.kernel.org
 help / color / mirror / Atom feed
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: 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.