mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove
@ 2026-09-11  6:55 Ziyou Wang (Lenovo)
  2026-10-30 14:54 ` Frank Li
       [not found] ` <CABBdrdLtTJL7-xgB4i=AvqBoBJfFzYvJe+gV+KyLz5=QjUWkCg@mail.gmail.com>
  0 siblings, 2 replies; 3+ messages in thread
From: Ziyou Wang (Lenovo) @ 2026-09-11  6:55 UTC (permalink / raw)
  To: Vinod Koul, Vinicius Costa Gomes
  Cc: Dave Jiang, Frank Li, dmaengine, linux-kernel,
	Ziyou Wang (Lenovo),
	Adrian Huang

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


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove
  2026-09-11  6:55 [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove Ziyou Wang (Lenovo)
@ 2026-10-30 14:54 ` Frank Li
       [not found] ` <CABBdrdLtTJL7-xgB4i=AvqBoBJfFzYvJe+gV+KyLz5=QjUWkCg@mail.gmail.com>
  1 sibling, 0 replies; 3+ messages in thread
From: Frank Li @ 2026-10-30 14:54 UTC (permalink / raw)
  To: Ziyou Wang (Lenovo)
  Cc: Vinod Koul, Vinicius Costa Gomes, Dave Jiang, Frank Li,
	dmaengine, linux-kernel, Adrian Huang

On Fri, Sep 11, 2026 at 02:55:47PM +0800, Ziyou Wang (Lenovo) wrote:
> [You don't often get email from ziyou.dev@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> 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

Can you trim dump to make commit message short.

>
> 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

This is typical unbind driver method. So needn't this paragraph.

Frank

>
> 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
>

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove
       [not found] ` <CABBdrdLtTJL7-xgB4i=AvqBoBJfFzYvJe+gV+KyLz5=QjUWkCg@mail.gmail.com>
@ 2026-10-31  0:13   ` Vinicius Costa Gomes
  0 siblings, 0 replies; 3+ messages in thread
From: Vinicius Costa Gomes @ 2026-10-31  0:13 UTC (permalink / raw)
  To: Ziyou Wang, Vinod Koul
  Cc: Dave Jiang, Frank Li, dmaengine, linux-kernel, Adrian Huang

Hi Ziyou,

Ziyou Wang <ziyou.dev@gmail.com> writes:

> Hi Vinod,
>
> Gentle ping on this patch.
>
> This fixes a use-after-free bug that can be triggered when unbinding
> the idxd driver, which results in a kernel crash. The fix has been
> tested on real hardware (Lenovo WenTian WR5220 G5) and verified with
> KASAN.
>
> The issue is critical as it causes general protection fault during
> driver unbind. The fix is straightforward - taking reference counts
> on wq/engine/group conf_dev before unregistering to prevent premature
> freeing.
>
> Please let me know if any changes are needed or if you need more
> information.
>

It looks very similar to the issue that

https://lore.kernel.org/all/20260415095030.42183-1-kanie@linux.alibaba.com/

tries to fix, can you confirm that it solves the issue you are seeing?


Cheers,
-- 
Vinicius

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-31  0:13 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-11  6:55 [PATCH] dmaengine: idxd: Fix use-after-free in idxd_remove Ziyou Wang (Lenovo)
2026-10-30 14:54 ` Frank Li
     [not found] ` <CABBdrdLtTJL7-xgB4i=AvqBoBJfFzYvJe+gV+KyLz5=QjUWkCg@mail.gmail.com>
2026-10-31  0:13   ` Vinicius Costa Gomes

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®