* [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl
@ 2026-06-14 22:16 Shuangpeng Bai
2026-06-15 2:39 ` Greg KH
` (2 more replies)
0 siblings, 3 replies; 11+ messages in thread
From: Shuangpeng Bai @ 2026-06-14 22:16 UTC (permalink / raw)
To: mani, kwilczynski, kishon, arnd, gregkh, linux-pci, linux-kernel
Hi Kernel Maintainers,
I hit the following report while testing current upstream kernel:
KASAN: slab-use-after-free in pci_endpoint_test_ioctl
I reproduced this on commit: e8c2f9fdadee7cbc75134dc463c1e0d856d6e5c7 (May 25 2026)
The reproducer and .config files are here.
https://gist.github.com/shuangpengbai/1e03464adfefbb75fb5d8112691e38b3
I'm happy to test debug patches or provide additional information.
Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com>
[ 222.367023][ T8376] BUG: KASAN: slab-use-after-free in pci_endpoint_test_ioctl (drivers/misc/pci_endpoint_test.c:1145)
[ 222.369439][ T8376] Read of size 8 at addr ffff88811be68438 by task pci_endpoint_te/8376
[ 222.371748][ T8376]
[ 222.372441][ T8376] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 222.372449][ T8376] Call Trace:
[ 222.372455][ T8376] <TASK>
[ 222.372460][ T8376] dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120)
[ 222.372476][ T8376] print_report (mm/kasan/report.c:378 mm/kasan/report.c:482)
[ 222.372531][ T8376] kasan_report (mm/kasan/report.c:595)
[ 222.372558][ T8376] pci_endpoint_test_ioctl (drivers/misc/pci_endpoint_test.c:1145)
[ 222.372604][ T8376] __se_sys_ioctl (fs/ioctl.c:51 fs/ioctl.c:597 fs/ioctl.c:583)
[ 222.372615][ T8376] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 222.372647][ T8376] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[ 222.372658][ T8376] RIP: 0033:0x7fd68c893237
[ 222.372668][ T8376] Code: 00 00 00 48 8b 05 59 cc 0d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 29 cc 0d 00 f7 d8 64 89 01 48
[ 222.372678][ T8376] RSP: 002b:00007ffc1aa73f18 EFLAGS: 00000206 ORIG_RAX: 0000000000000010
[ 222.372693][ T8376] RAX: ffffffffffffffda RBX: 00007fd68c9774c0 RCX: 00007fd68c893237
[ 222.372701][ T8376] RDX: 0000000000000000 RSI: 0000000000005009 RDI: 0000000000000003
[ 222.372707][ T8376] RBP: 0000560e1af360c3 R08: 0000000000000000 R09: 00007ffc1aa73e27
[ 222.372714][ T8376] R10: fffffffffffff64d R11: 0000000000000206 R12: 0000000000000003
[ 222.372719][ T8376] R13: 0000000000005009 R14: 0000000000000000 R15: 00007ffc1aa73f80
[ 222.372731][ T8376] </TASK>
[ 222.372735][ T8376]
[ 222.392098][ T8376] Freed by task 8376 on cpu 0 at 222.263660s:
[ 222.392729][ T8376] kasan_save_track (mm/kasan/common.c:57 mm/kasan/common.c:78)
[ 222.393215][ T8376] kasan_save_free_info (mm/kasan/generic.c:584)
[ 222.393743][ T8376] __kasan_slab_free (mm/kasan/common.c:253 mm/kasan/common.c:285)
[ 222.394242][ T8376] kfree (include/linux/kasan.h:235 mm/slub.c:2689 mm/slub.c:6251 mm/slub.c:6566)
[ 222.394657][ T8376] devres_release_all (drivers/base/devres.c:50 drivers/base/devres.c:547 drivers/base/devres.c:576)
[ 222.395176][ T8376] device_release_driver_internal (drivers/base/dd.c:598 drivers/base/dd.c:1357 drivers/base/dd.c:1375)
[ 222.395807][ T8376] unbind_store (drivers/base/bus.c:244)
[ 222.396280][ T8376] kernfs_fop_write_iter (fs/kernfs/file.c:352)
[ 222.396832][ T8376] vfs_write (fs/read_write.c:595 fs/read_write.c:688)
[ 222.397273][ T8376] ksys_write (fs/read_write.c:740)
[ 222.397725][ T8376] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 222.398207][ T8376] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121)
[ 222.398823][ T8376]
[ 222.399073][ T8376] The buggy address belongs to the object at ffff88811be68400
[ 222.399073][ T8376] which belongs to the cache kmalloc-512 of size 512
[ 222.400509][ T8376] The buggy address is located 56 bytes inside of
[ 222.400509][ T8376] freed 512-byte region [ffff88811be68400, ffff88811be68600)
[ 222.401905][ T8376]
Best,
Shuangpeng
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl 2026-06-14 22:16 [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl Shuangpeng Bai @ 2026-06-15 2:39 ` Greg KH 2026-06-15 20:06 ` Shuangpeng 2026-06-18 3:01 ` Krzysztof Wilczyński 2026-06-21 5:46 ` [PATCH] misc: pci_endpoint_test: fix use-after-free after device unbind Shuangpeng Bai 2 siblings, 1 reply; 11+ messages in thread From: Greg KH @ 2026-06-15 2:39 UTC (permalink / raw) To: Shuangpeng Bai; +Cc: mani, kwilczynski, kishon, arnd, linux-pci, linux-kernel On Sun, Jun 14, 2026 at 06:16:39PM -0400, Shuangpeng Bai wrote: > Hi Kernel Maintainers, > > I hit the following report while testing current upstream kernel: > > KASAN: slab-use-after-free in pci_endpoint_test_ioctl > > I reproduced this on commit: e8c2f9fdadee7cbc75134dc463c1e0d856d6e5c7 (May 25 2026) > > The reproducer and .config files are here. > https://gist.github.com/shuangpengbai/1e03464adfefbb75fb5d8112691e38b3 > > I'm happy to test debug patches or provide additional information. As we are drowning in real/fake/unknown bug reports, why not take the time to work on a patch to fix these issues as you have a reproducer for them? That way you get full credit for the fix. thanks, greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl 2026-06-15 2:39 ` Greg KH @ 2026-06-15 20:06 ` Shuangpeng 0 siblings, 0 replies; 11+ messages in thread From: Shuangpeng @ 2026-06-15 20:06 UTC (permalink / raw) To: Greg KH; +Cc: mani, kwilczynski, kishon, arnd, linux-pci, linux-kernel > On Jun 14, 2026, at 22:39, Greg KH <gregkh@linuxfoundation.org> wrote: > > On Sun, Jun 14, 2026 at 06:16:39PM -0400, Shuangpeng Bai wrote: >> Hi Kernel Maintainers, >> >> I hit the following report while testing current upstream kernel: >> >> KASAN: slab-use-after-free in pci_endpoint_test_ioctl >> >> I reproduced this on commit: e8c2f9fdadee7cbc75134dc463c1e0d856d6e5c7 (May 25 2026) >> >> The reproducer and .config files are here. >> https://gist.github.com/shuangpengbai/1e03464adfefbb75fb5d8112691e38b3 >> >> I'm happy to test debug patches or provide additional information. > > As we are drowning in real/fake/unknown bug reports, why not take the > time to work on a patch to fix these issues as you have a reproducer for > them? That way you get full credit for the fix. > Thanks for your suggestion. I will try to look into this and understand the root cause. If I can identify a proper fix, I will send a patch for review. Thanks, Shuangpeng > thanks, > > greg k-h ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl 2026-06-14 22:16 [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl Shuangpeng Bai 2026-06-15 2:39 ` Greg KH @ 2026-06-18 3:01 ` Krzysztof Wilczyński 2026-06-21 5:46 ` [PATCH] misc: pci_endpoint_test: fix use-after-free after device unbind Shuangpeng Bai 2 siblings, 0 replies; 11+ messages in thread From: Krzysztof Wilczyński @ 2026-06-18 3:01 UTC (permalink / raw) To: Shuangpeng Bai; +Cc: mani, kishon, arnd, gregkh, linux-pci, linux-kernel Hello, > I hit the following report while testing current upstream kernel: > > KASAN: slab-use-after-free in pci_endpoint_test_ioctl > > I reproduced this on commit: e8c2f9fdadee7cbc75134dc463c1e0d856d6e5c7 (May 25 2026) > > The reproducer and .config files are here. > https://gist.github.com/shuangpengbai/1e03464adfefbb75fb5d8112691e38b3 The proof-of-concept you have there can be distilled down to: #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> #include <unistd.h> #define BDF "0000:XX:YY.Z" int main(void) { int fd = open("/dev/pci-endpoint-test.0", O_RDWR); int u = open("/sys/bus/pci/drivers/pci-endpoint-test/unbind", O_WRONLY); if (write(u, BDF, strlen(BDF)) < 0) return 1; ioctl(fd, 0); return 0; } Only required steps would be to update the BDF definition with the relevant device address, and then bind the right device prior to running the proof-of-concept binary: $ echo "1234 0987" > /sys/bus/pci/drivers/pci-endpoint-test/new_id > [ 222.372449][ T8376] Call Trace: > [ 222.372455][ T8376] <TASK> > [ 222.372460][ T8376] dump_stack_lvl (lib/dump_stack.c:94 lib/dump_stack.c:120) > [ 222.372476][ T8376] print_report (mm/kasan/report.c:378 mm/kasan/report.c:482) > [ 222.372531][ T8376] kasan_report (mm/kasan/report.c:595) > [ 222.372558][ T8376] pci_endpoint_test_ioctl (drivers/misc/pci_endpoint_test.c:1145) > [ 222.372604][ T8376] __se_sys_ioctl (fs/ioctl.c:51 fs/ioctl.c:597 fs/ioctl.c:583) > [ 222.372615][ T8376] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) > [ 222.372647][ T8376] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) > [ 222.372658][ T8376] RIP: 0033:0x7fd68c893237 > [ 222.372668][ T8376] Code: 00 00 00 48 8b 05 59 cc 0d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 29 cc 0d 00 f7 d8 64 89 01 48 > [ 222.372678][ T8376] RSP: 002b:00007ffc1aa73f18 EFLAGS: 00000206 ORIG_RAX: 0000000000000010 > [ 222.372693][ T8376] RAX: ffffffffffffffda RBX: 00007fd68c9774c0 RCX: 00007fd68c893237 > [ 222.372701][ T8376] RDX: 0000000000000000 RSI: 0000000000005009 RDI: 0000000000000003 > [ 222.372707][ T8376] RBP: 0000560e1af360c3 R08: 0000000000000000 R09: 00007ffc1aa73e27 > [ 222.372714][ T8376] R10: fffffffffffff64d R11: 0000000000000206 R12: 0000000000000003 > [ 222.372719][ T8376] R13: 0000000000005009 R14: 0000000000000000 R15: 00007ffc1aa73f80 > [ 222.372731][ T8376] </TASK> > [ 222.372735][ T8376] > [ 222.392098][ T8376] Freed by task 8376 on cpu 0 at 222.263660s: > [ 222.392729][ T8376] kasan_save_track (mm/kasan/common.c:57 mm/kasan/common.c:78) > [ 222.393215][ T8376] kasan_save_free_info (mm/kasan/generic.c:584) > [ 222.393743][ T8376] __kasan_slab_free (mm/kasan/common.c:253 mm/kasan/common.c:285) > [ 222.394242][ T8376] kfree (include/linux/kasan.h:235 mm/slub.c:2689 mm/slub.c:6251 mm/slub.c:6566) > [ 222.394657][ T8376] devres_release_all (drivers/base/devres.c:50 drivers/base/devres.c:547 drivers/base/devres.c:576) > [ 222.395176][ T8376] device_release_driver_internal (drivers/base/dd.c:598 drivers/base/dd.c:1357 drivers/base/dd.c:1375) > [ 222.395807][ T8376] unbind_store (drivers/base/bus.c:244) > [ 222.396280][ T8376] kernfs_fop_write_iter (fs/kernfs/file.c:352) > [ 222.396832][ T8376] vfs_write (fs/read_write.c:595 fs/read_write.c:688) > [ 222.397273][ T8376] ksys_write (fs/read_write.c:740) > [ 222.397725][ T8376] do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94) > [ 222.398207][ T8376] entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) > [ 222.398823][ T8376] > [ 222.399073][ T8376] The buggy address belongs to the object at ffff88811be68400 > [ 222.399073][ T8376] which belongs to the cache kmalloc-512 of size 512 > [ 222.400509][ T8376] The buggy address is located 56 bytes inside of > [ 222.400509][ T8376] freed 512-byte region [ffff88811be68400, ffff88811be68600) At first glance, it looks like the file descriptor outlives the driver binding. An open file descriptor doesn't block unbind via sysfs, and misc_deregister() doesn't revoke currently open file descriptors either. The struct pci_endpoint_test has to stay alive until both the driver is unbound and the last open file descriptor is closed - whichever happens last, whereas devres frees it as soon as the driver is unbound, regardless of any file descriptor that is still open. Hence the dangling reference and the use-after-free. Thank you! Krzysztof ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-14 22:16 [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl Shuangpeng Bai 2026-06-15 2:39 ` Greg KH 2026-06-18 3:01 ` Krzysztof Wilczyński @ 2026-06-21 5:46 ` Shuangpeng Bai 2026-06-22 16:52 ` [PATCH v2] " Shuangpeng Bai 2 siblings, 1 reply; 11+ messages in thread From: Shuangpeng Bai @ 2026-06-21 5:46 UTC (permalink / raw) To: mani, kwilczynski Cc: kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel, Shuangpeng Bai An open miscdevice file descriptor can outlive the PCI driver binding. misc_deregister() removes the device node and prevents new opens, but it does not revoke file descriptors that are already open. Before this change, pci_endpoint_test stored the miscdevice inside struct pci_endpoint_test, and ioctl() recovered the test object from file->private_data with container_of(). Since the test object was allocated with devm_kzalloc(), it was freed when the PCI device was unbound. A process could therefore open /dev/pci-endpoint-test.N, unbind the PCI device through sysfs, and then issue an ioctl on the stale file descriptor, causing a use-after-free of struct pci_endpoint_test. Manage the test object lifetime explicitly. Allocate it with kzalloc_obj(), add a kref, take a reference from .open(), and drop it from .release(). The remove path deregisters the miscdevice to stop new opens, serializes with ioctl using the existing mutex, releases the PCI resources, clears test->pdev, and drops the initial reference. Already-open file descriptors then keep the test object alive; subsequent ioctls fail with -ENODEV instead of touching released device resources. Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device") Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> Closes: https://lore.kernel.org/all/178144969601.60470.7358419009914000395@gmail.com/ Suggested-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> --- drivers/misc/pci_endpoint_test.c | 61 ++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index dbd017cabbb9..981a834ce41b 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -13,6 +13,7 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/kref.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/mutex.h> @@ -136,6 +137,7 @@ enum pci_barno { }; struct pci_endpoint_test { + struct kref kref; struct pci_dev *pdev; void __iomem *base; void __iomem *bar[PCI_STD_NUM_BARS]; @@ -143,7 +145,7 @@ struct pci_endpoint_test { int last_irq; int num_irqs; int irq_type; - /* mutex to protect the ioctls */ + /* mutex to serialize ioctls and removal */ struct mutex mutex; struct miscdevice miscdev; enum pci_barno test_reg_bar; @@ -157,6 +159,15 @@ struct pci_endpoint_test_data { size_t alignment; }; +static void pci_endpoint_test_free(struct kref *kref) +{ + struct pci_endpoint_test *test = container_of(kref, + struct pci_endpoint_test, + kref); + + kfree(test); +} + static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, u32 offset) { @@ -1141,10 +1152,15 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, { int ret = -EINVAL; enum pci_barno bar; - struct pci_endpoint_test *test = to_endpoint_test(file->private_data); - struct pci_dev *pdev = test->pdev; + struct pci_endpoint_test *test = file->private_data; + struct pci_dev *pdev; mutex_lock(&test->mutex); + pdev = test->pdev; + if (!pdev) { + ret = -ENODEV; + goto ret; + } reinit_completion(&test->irq_raised); test->last_irq = -ENODATA; @@ -1206,9 +1222,30 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, return ret; } +static int pci_endpoint_test_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct pci_endpoint_test *test = to_endpoint_test(miscdev); + + kref_get(&test->kref); + file->private_data = test; + + return 0; +} + +static int pci_endpoint_test_release(struct inode *inode, struct file *file) +{ + struct pci_endpoint_test *test = file->private_data; + + kref_put(&test->kref, pci_endpoint_test_free); + return 0; +} + static const struct file_operations pci_endpoint_test_fops = { .owner = THIS_MODULE, + .open = pci_endpoint_test_open, .unlocked_ioctl = pci_endpoint_test_ioctl, + .release = pci_endpoint_test_release, }; static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test) @@ -1241,10 +1278,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, if (pci_is_bridge(pdev)) return -ENODEV; - test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL); + test = kzalloc_obj(*test); if (!test) return -ENOMEM; + kref_init(&test->kref); test->pdev = pdev; test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; @@ -1263,7 +1301,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) { dev_err(dev, "Cannot enable PCI device\n"); - return ret; + goto err_kfree_test; } ret = pci_request_regions(pdev, DRV_MODULE_NAME); @@ -1349,6 +1387,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, err_disable_pdev: pci_disable_device(pdev); +err_kfree_test: + kfree(test); + return ret; } @@ -1364,10 +1405,13 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) if (id < 0) return; + misc_deregister(&test->miscdev); + + mutex_lock(&test->mutex); + pci_endpoint_test_release_irq(test); pci_endpoint_test_free_irq_vectors(test); - misc_deregister(&test->miscdev); kfree(misc_device->name); kfree(test->name); ida_free(&pci_endpoint_test_ida, id); @@ -1378,6 +1422,11 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + test->pdev = NULL; + mutex_unlock(&test->mutex); + + kref_put(&test->kref, pci_endpoint_test_free); } static const struct pci_endpoint_test_data default_data = { -- 2.34.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-21 5:46 ` [PATCH] misc: pci_endpoint_test: fix use-after-free after device unbind Shuangpeng Bai @ 2026-06-22 16:52 ` Shuangpeng Bai 2026-06-22 20:40 ` Frank Li 2026-06-27 13:16 ` Manivannan Sadhasivam 0 siblings, 2 replies; 11+ messages in thread From: Shuangpeng Bai @ 2026-06-22 16:52 UTC (permalink / raw) To: mani, kwilczynski Cc: kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel, Shuangpeng Bai An open miscdevice file descriptor can outlive the PCI driver binding. misc_deregister() removes the device node and prevents new opens, but it does not revoke file descriptors that are already open. Before this change, pci_endpoint_test stored the miscdevice inside struct pci_endpoint_test, and ioctl() recovered the test object from file->private_data with container_of(). Since the test object was allocated with devm_kzalloc(), it was freed when the PCI device was unbound. A process could therefore open /dev/pci-endpoint-test.N, unbind the PCI device through sysfs, and then issue an ioctl on the stale file descriptor, causing a use-after-free of struct pci_endpoint_test. Manage the test object lifetime explicitly. Allocate it with kzalloc_obj(), add a kref, take a reference from .open(), and drop it from .release(). The remove path deregisters the miscdevice to stop new opens, serializes with ioctl using the existing mutex, releases the PCI resources, clears test->pdev, and drops the probe reference. Use the same 1s timeout already used by the other endpoint interrupt tests for the READ, WRITE and COPY completion waits. This prevents device unbind from blocking indefinitely behind an ioctl waiting for a missing endpoint interrupt. Already-open file descriptors then keep the test object alive; subsequent ioctls fail with -ENODEV instead of touching released device resources. Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device") Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> Closes: https://lore.kernel.org/all/178144969601.60470.7358419009914000395@gmail.com/ Suggested-by: Krzysztof Wilczyński <kwilczynski@kernel.org> Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> --- Changes in v2: - Bound READ, WRITE and COPY completion waits with the same 1s timeout used by the other endpoint interrupt tests so device unbind cannot block indefinitely behind an ioctl waiting for a missing endpoint interrupt. Please let me know if there are any concerns with this approach or if there is a simpler preferred way to handle this lifetime. --- drivers/misc/pci_endpoint_test.c | 86 ++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 9 deletions(-) diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index dbd017cabbb9..65d768dab450 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -13,6 +13,7 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/kref.h> #include <linux/miscdevice.h> #include <linux/module.h> #include <linux/mutex.h> @@ -136,6 +137,7 @@ enum pci_barno { }; struct pci_endpoint_test { + struct kref kref; struct pci_dev *pdev; void __iomem *base; void __iomem *bar[PCI_STD_NUM_BARS]; @@ -143,7 +145,7 @@ struct pci_endpoint_test { int last_irq; int num_irqs; int irq_type; - /* mutex to protect the ioctls */ + /* mutex to serialize ioctls and removal */ struct mutex mutex; struct miscdevice miscdev; enum pci_barno test_reg_bar; @@ -157,6 +159,15 @@ struct pci_endpoint_test_data { size_t alignment; }; +static void pci_endpoint_test_free(struct kref *kref) +{ + struct pci_endpoint_test *test = container_of(kref, + struct pci_endpoint_test, + kref); + + kfree(test); +} + static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, u32 offset) { @@ -804,10 +815,17 @@ static int pci_endpoint_test_copy(struct pci_endpoint_test *test, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, COMMAND_COPY); - wait_for_completion(&test->irq_raised); + if (!wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000))) { + ret = -ETIMEDOUT; + goto err_dst_unmap; + } +err_dst_unmap: dma_unmap_single(dev, orig_dst_phys_addr, size + alignment, DMA_FROM_DEVICE); + if (ret) + goto err_dst_phys_addr; dst_crc32 = crc32_le(~0, dst_addr, size); if (dst_crc32 != src_crc32) @@ -909,12 +927,17 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, COMMAND_READ); - wait_for_completion(&test->irq_raised); + if (!wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000))) { + ret = -ETIMEDOUT; + goto err_unmap_addr; + } reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); if (!(reg & STATUS_READ_SUCCESS)) ret = -EIO; +err_unmap_addr: dma_unmap_single(dev, orig_phys_addr, size + alignment, DMA_TO_DEVICE); @@ -1000,10 +1023,17 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test, pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, COMMAND_WRITE); - wait_for_completion(&test->irq_raised); + if (!wait_for_completion_timeout(&test->irq_raised, + msecs_to_jiffies(1000))) { + ret = -ETIMEDOUT; + goto err_unmap_addr; + } +err_unmap_addr: dma_unmap_single(dev, orig_phys_addr, size + alignment, DMA_FROM_DEVICE); + if (ret) + goto err_phys_addr; crc32 = crc32_le(~0, addr, size); if (crc32 != pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM)) @@ -1141,10 +1171,15 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, { int ret = -EINVAL; enum pci_barno bar; - struct pci_endpoint_test *test = to_endpoint_test(file->private_data); - struct pci_dev *pdev = test->pdev; + struct pci_endpoint_test *test = file->private_data; + struct pci_dev *pdev; mutex_lock(&test->mutex); + pdev = test->pdev; + if (!pdev) { + ret = -ENODEV; + goto ret; + } reinit_completion(&test->irq_raised); test->last_irq = -ENODATA; @@ -1206,9 +1241,30 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, return ret; } +static int pci_endpoint_test_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct pci_endpoint_test *test = to_endpoint_test(miscdev); + + kref_get(&test->kref); + file->private_data = test; + + return 0; +} + +static int pci_endpoint_test_release(struct inode *inode, struct file *file) +{ + struct pci_endpoint_test *test = file->private_data; + + kref_put(&test->kref, pci_endpoint_test_free); + return 0; +} + static const struct file_operations pci_endpoint_test_fops = { .owner = THIS_MODULE, + .open = pci_endpoint_test_open, .unlocked_ioctl = pci_endpoint_test_ioctl, + .release = pci_endpoint_test_release, }; static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test) @@ -1241,10 +1297,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, if (pci_is_bridge(pdev)) return -ENODEV; - test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL); + test = kzalloc_obj(*test); if (!test) return -ENOMEM; + kref_init(&test->kref); test->pdev = pdev; test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; @@ -1263,7 +1320,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, ret = pci_enable_device(pdev); if (ret) { dev_err(dev, "Cannot enable PCI device\n"); - return ret; + goto err_kfree_test; } ret = pci_request_regions(pdev, DRV_MODULE_NAME); @@ -1349,6 +1406,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, err_disable_pdev: pci_disable_device(pdev); +err_kfree_test: + kfree(test); + return ret; } @@ -1364,10 +1424,13 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) if (id < 0) return; + misc_deregister(&test->miscdev); + + mutex_lock(&test->mutex); + pci_endpoint_test_release_irq(test); pci_endpoint_test_free_irq_vectors(test); - misc_deregister(&test->miscdev); kfree(misc_device->name); kfree(test->name); ida_free(&pci_endpoint_test_ida, id); @@ -1378,6 +1441,11 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) pci_release_regions(pdev); pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + test->pdev = NULL; + mutex_unlock(&test->mutex); + + kref_put(&test->kref, pci_endpoint_test_free); } static const struct pci_endpoint_test_data default_data = { -- 2.34.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-22 16:52 ` [PATCH v2] " Shuangpeng Bai @ 2026-06-22 20:40 ` Frank Li 2026-06-24 4:07 ` Shuangpeng 2026-06-27 12:59 ` Manivannan Sadhasivam 2026-06-27 13:16 ` Manivannan Sadhasivam 1 sibling, 2 replies; 11+ messages in thread From: Frank Li @ 2026-06-22 20:40 UTC (permalink / raw) To: Shuangpeng Bai Cc: mani, kwilczynski, kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel On Mon, Jun 22, 2026 at 12:52:49PM -0400, Shuangpeng Bai wrote: > [You don't often get email from shuangpeng.kernel@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > An open miscdevice file descriptor can outlive the PCI driver binding. > misc_deregister() removes the device node and prevents new opens, but it > does not revoke file descriptors that are already open. > > Before this change, pci_endpoint_test stored the miscdevice inside struct > pci_endpoint_test, and ioctl() recovered the test object from > file->private_data with container_of(). Since the test object was allocated > with devm_kzalloc(), it was freed when the PCI device was unbound. A > process could therefore open /dev/pci-endpoint-test.N, unbind the PCI > device through sysfs, and then issue an ioctl on the stale file descriptor, > causing a use-after-free of struct pci_endpoint_test. Anyways to provent unbound when still open? This driver is only for test ednpoint funciton, it is not worth to make complex for this unusual case. Frank > > Manage the test object lifetime explicitly. Allocate it with kzalloc_obj(), > add a kref, take a reference from .open(), and drop it from .release(). > The remove path deregisters the miscdevice to stop new opens, serializes > with ioctl using the existing mutex, releases the PCI resources, clears > test->pdev, and drops the probe reference. > > Use the same 1s timeout already used by the other endpoint interrupt tests > for the READ, WRITE and COPY completion waits. This prevents device unbind > from blocking indefinitely behind an ioctl waiting for a missing endpoint > interrupt. Already-open file descriptors then keep the test object alive; > subsequent ioctls fail with -ENODEV instead of touching released device > resources. > > Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device") > Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> > Closes: https://lore.kernel.org/all/178144969601.60470.7358419009914000395@gmail.com/ > Suggested-by: Krzysztof Wilczyński <kwilczynski@kernel.org> > Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> > --- > Changes in v2: > - Bound READ, WRITE and COPY completion waits with the same 1s timeout used > by the other endpoint interrupt tests so device unbind cannot block > indefinitely behind an ioctl waiting for a missing endpoint interrupt. > > Please let me know if there are any concerns with this approach or if there > is a simpler preferred way to handle this lifetime. > > --- > drivers/misc/pci_endpoint_test.c | 86 ++++++++++++++++++++++++++++---- > 1 file changed, 77 insertions(+), 9 deletions(-) > > diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c > index dbd017cabbb9..65d768dab450 100644 > --- a/drivers/misc/pci_endpoint_test.c > +++ b/drivers/misc/pci_endpoint_test.c > @@ -13,6 +13,7 @@ > #include <linux/io.h> > #include <linux/interrupt.h> > #include <linux/irq.h> > +#include <linux/kref.h> > #include <linux/miscdevice.h> > #include <linux/module.h> > #include <linux/mutex.h> > @@ -136,6 +137,7 @@ enum pci_barno { > }; > > struct pci_endpoint_test { > + struct kref kref; > struct pci_dev *pdev; > void __iomem *base; > void __iomem *bar[PCI_STD_NUM_BARS]; > @@ -143,7 +145,7 @@ struct pci_endpoint_test { > int last_irq; > int num_irqs; > int irq_type; > - /* mutex to protect the ioctls */ > + /* mutex to serialize ioctls and removal */ > struct mutex mutex; > struct miscdevice miscdev; > enum pci_barno test_reg_bar; > @@ -157,6 +159,15 @@ struct pci_endpoint_test_data { > size_t alignment; > }; > > +static void pci_endpoint_test_free(struct kref *kref) > +{ > + struct pci_endpoint_test *test = container_of(kref, > + struct pci_endpoint_test, > + kref); > + > + kfree(test); > +} > + > static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, > u32 offset) > { > @@ -804,10 +815,17 @@ static int pci_endpoint_test_copy(struct pci_endpoint_test *test, > pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, > COMMAND_COPY); > > - wait_for_completion(&test->irq_raised); > + if (!wait_for_completion_timeout(&test->irq_raised, > + msecs_to_jiffies(1000))) { > + ret = -ETIMEDOUT; > + goto err_dst_unmap; > + } > > +err_dst_unmap: > dma_unmap_single(dev, orig_dst_phys_addr, size + alignment, > DMA_FROM_DEVICE); > + if (ret) > + goto err_dst_phys_addr; > > dst_crc32 = crc32_le(~0, dst_addr, size); > if (dst_crc32 != src_crc32) > @@ -909,12 +927,17 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test, > pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, > COMMAND_READ); > > - wait_for_completion(&test->irq_raised); > + if (!wait_for_completion_timeout(&test->irq_raised, > + msecs_to_jiffies(1000))) { > + ret = -ETIMEDOUT; > + goto err_unmap_addr; > + } > > reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); > if (!(reg & STATUS_READ_SUCCESS)) > ret = -EIO; > > +err_unmap_addr: > dma_unmap_single(dev, orig_phys_addr, size + alignment, > DMA_TO_DEVICE); > > @@ -1000,10 +1023,17 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test, > pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, > COMMAND_WRITE); > > - wait_for_completion(&test->irq_raised); > + if (!wait_for_completion_timeout(&test->irq_raised, > + msecs_to_jiffies(1000))) { > + ret = -ETIMEDOUT; > + goto err_unmap_addr; > + } > > +err_unmap_addr: > dma_unmap_single(dev, orig_phys_addr, size + alignment, > DMA_FROM_DEVICE); > + if (ret) > + goto err_phys_addr; > > crc32 = crc32_le(~0, addr, size); > if (crc32 != pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM)) > @@ -1141,10 +1171,15 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, > { > int ret = -EINVAL; > enum pci_barno bar; > - struct pci_endpoint_test *test = to_endpoint_test(file->private_data); > - struct pci_dev *pdev = test->pdev; > + struct pci_endpoint_test *test = file->private_data; > + struct pci_dev *pdev; > > mutex_lock(&test->mutex); > + pdev = test->pdev; > + if (!pdev) { > + ret = -ENODEV; > + goto ret; > + } > > reinit_completion(&test->irq_raised); > test->last_irq = -ENODATA; > @@ -1206,9 +1241,30 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, > return ret; > } > > +static int pci_endpoint_test_open(struct inode *inode, struct file *file) > +{ > + struct miscdevice *miscdev = file->private_data; > + struct pci_endpoint_test *test = to_endpoint_test(miscdev); > + > + kref_get(&test->kref); > + file->private_data = test; > + > + return 0; > +} > + > +static int pci_endpoint_test_release(struct inode *inode, struct file *file) > +{ > + struct pci_endpoint_test *test = file->private_data; > + > + kref_put(&test->kref, pci_endpoint_test_free); > + return 0; > +} > + > static const struct file_operations pci_endpoint_test_fops = { > .owner = THIS_MODULE, > + .open = pci_endpoint_test_open, > .unlocked_ioctl = pci_endpoint_test_ioctl, > + .release = pci_endpoint_test_release, > }; > > static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test) > @@ -1241,10 +1297,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, > if (pci_is_bridge(pdev)) > return -ENODEV; > > - test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL); > + test = kzalloc_obj(*test); > if (!test) > return -ENOMEM; > > + kref_init(&test->kref); > test->pdev = pdev; > test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; > > @@ -1263,7 +1320,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, > ret = pci_enable_device(pdev); > if (ret) { > dev_err(dev, "Cannot enable PCI device\n"); > - return ret; > + goto err_kfree_test; > } > > ret = pci_request_regions(pdev, DRV_MODULE_NAME); > @@ -1349,6 +1406,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, > err_disable_pdev: > pci_disable_device(pdev); > > +err_kfree_test: > + kfree(test); > + > return ret; > } > > @@ -1364,10 +1424,13 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) > if (id < 0) > return; > > + misc_deregister(&test->miscdev); > + > + mutex_lock(&test->mutex); > + > pci_endpoint_test_release_irq(test); > pci_endpoint_test_free_irq_vectors(test); > > - misc_deregister(&test->miscdev); > kfree(misc_device->name); > kfree(test->name); > ida_free(&pci_endpoint_test_ida, id); > @@ -1378,6 +1441,11 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) > > pci_release_regions(pdev); > pci_disable_device(pdev); > + pci_set_drvdata(pdev, NULL); > + test->pdev = NULL; > + mutex_unlock(&test->mutex); > + > + kref_put(&test->kref, pci_endpoint_test_free); > } > > static const struct pci_endpoint_test_data default_data = { > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-22 20:40 ` Frank Li @ 2026-06-24 4:07 ` Shuangpeng 2026-06-27 12:59 ` Manivannan Sadhasivam 1 sibling, 0 replies; 11+ messages in thread From: Shuangpeng @ 2026-06-24 4:07 UTC (permalink / raw) To: Frank Li Cc: mani, kwilczynski, kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel > On Jun 22, 2026, at 16:40, Frank Li <Frank.li@oss.nxp.com> wrote: > > On Mon, Jun 22, 2026 at 12:52:49PM -0400, Shuangpeng Bai wrote: >> [You don't often get email from shuangpeng.kernel@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] >> >> An open miscdevice file descriptor can outlive the PCI driver binding. >> misc_deregister() removes the device node and prevents new opens, but it >> does not revoke file descriptors that are already open. >> >> Before this change, pci_endpoint_test stored the miscdevice inside struct >> pci_endpoint_test, and ioctl() recovered the test object from >> file->private_data with container_of(). Since the test object was allocated >> with devm_kzalloc(), it was freed when the PCI device was unbound. A >> process could therefore open /dev/pci-endpoint-test.N, unbind the PCI >> device through sysfs, and then issue an ioctl on the stale file descriptor, >> causing a use-after-free of struct pci_endpoint_test. > > Anyways to provent unbound when still open? This driver is only for test > ednpoint funciton, it is not worth to make complex for this unusual case. > Thanks, Frank. Agreed. I looked at preventing unbind while the miscdevice is still open, but I do not see a simple way to do that from this PCI driver. The other approaches make the fix more complicated than the issue warrants for this test endpoint driver. I'll drop this patch for now. If there is a simpler preferred approach, I would be happy to look at it. Thanks, Shuangpeng > Frank > >> >> Manage the test object lifetime explicitly. Allocate it with kzalloc_obj(), >> add a kref, take a reference from .open(), and drop it from .release(). >> The remove path deregisters the miscdevice to stop new opens, serializes >> with ioctl using the existing mutex, releases the PCI resources, clears >> test->pdev, and drops the probe reference. >> >> Use the same 1s timeout already used by the other endpoint interrupt tests >> for the READ, WRITE and COPY completion waits. This prevents device unbind >> from blocking indefinitely behind an ioctl waiting for a missing endpoint >> interrupt. Already-open file descriptors then keep the test object alive; >> subsequent ioctls fail with -ENODEV instead of touching released device >> resources. >> >> Fixes: 2c156ac71c6b ("misc: Add host side PCI driver for PCI test function device") >> Reported-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> >> Closes: https://lore.kernel.org/all/178144969601.60470.7358419009914000395@gmail.com/ >> Suggested-by: Krzysztof Wilczyński <kwilczynski@kernel.org> >> Signed-off-by: Shuangpeng Bai <shuangpeng.kernel@gmail.com> >> --- >> Changes in v2: >> - Bound READ, WRITE and COPY completion waits with the same 1s timeout used >> by the other endpoint interrupt tests so device unbind cannot block >> indefinitely behind an ioctl waiting for a missing endpoint interrupt. >> >> Please let me know if there are any concerns with this approach or if there >> is a simpler preferred way to handle this lifetime. >> >> --- >> drivers/misc/pci_endpoint_test.c | 86 ++++++++++++++++++++++++++++---- >> 1 file changed, 77 insertions(+), 9 deletions(-) >> >> diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c >> index dbd017cabbb9..65d768dab450 100644 >> --- a/drivers/misc/pci_endpoint_test.c >> +++ b/drivers/misc/pci_endpoint_test.c >> @@ -13,6 +13,7 @@ >> #include <linux/io.h> >> #include <linux/interrupt.h> >> #include <linux/irq.h> >> +#include <linux/kref.h> >> #include <linux/miscdevice.h> >> #include <linux/module.h> >> #include <linux/mutex.h> >> @@ -136,6 +137,7 @@ enum pci_barno { >> }; >> >> struct pci_endpoint_test { >> + struct kref kref; >> struct pci_dev *pdev; >> void __iomem *base; >> void __iomem *bar[PCI_STD_NUM_BARS]; >> @@ -143,7 +145,7 @@ struct pci_endpoint_test { >> int last_irq; >> int num_irqs; >> int irq_type; >> - /* mutex to protect the ioctls */ >> + /* mutex to serialize ioctls and removal */ >> struct mutex mutex; >> struct miscdevice miscdev; >> enum pci_barno test_reg_bar; >> @@ -157,6 +159,15 @@ struct pci_endpoint_test_data { >> size_t alignment; >> }; >> >> +static void pci_endpoint_test_free(struct kref *kref) >> +{ >> + struct pci_endpoint_test *test = container_of(kref, >> + struct pci_endpoint_test, >> + kref); >> + >> + kfree(test); >> +} >> + >> static inline u32 pci_endpoint_test_readl(struct pci_endpoint_test *test, >> u32 offset) >> { >> @@ -804,10 +815,17 @@ static int pci_endpoint_test_copy(struct pci_endpoint_test *test, >> pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, >> COMMAND_COPY); >> >> - wait_for_completion(&test->irq_raised); >> + if (!wait_for_completion_timeout(&test->irq_raised, >> + msecs_to_jiffies(1000))) { >> + ret = -ETIMEDOUT; >> + goto err_dst_unmap; >> + } >> >> +err_dst_unmap: >> dma_unmap_single(dev, orig_dst_phys_addr, size + alignment, >> DMA_FROM_DEVICE); >> + if (ret) >> + goto err_dst_phys_addr; >> >> dst_crc32 = crc32_le(~0, dst_addr, size); >> if (dst_crc32 != src_crc32) >> @@ -909,12 +927,17 @@ static int pci_endpoint_test_write(struct pci_endpoint_test *test, >> pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, >> COMMAND_READ); >> >> - wait_for_completion(&test->irq_raised); >> + if (!wait_for_completion_timeout(&test->irq_raised, >> + msecs_to_jiffies(1000))) { >> + ret = -ETIMEDOUT; >> + goto err_unmap_addr; >> + } >> >> reg = pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_STATUS); >> if (!(reg & STATUS_READ_SUCCESS)) >> ret = -EIO; >> >> +err_unmap_addr: >> dma_unmap_single(dev, orig_phys_addr, size + alignment, >> DMA_TO_DEVICE); >> >> @@ -1000,10 +1023,17 @@ static int pci_endpoint_test_read(struct pci_endpoint_test *test, >> pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_COMMAND, >> COMMAND_WRITE); >> >> - wait_for_completion(&test->irq_raised); >> + if (!wait_for_completion_timeout(&test->irq_raised, >> + msecs_to_jiffies(1000))) { >> + ret = -ETIMEDOUT; >> + goto err_unmap_addr; >> + } >> >> +err_unmap_addr: >> dma_unmap_single(dev, orig_phys_addr, size + alignment, >> DMA_FROM_DEVICE); >> + if (ret) >> + goto err_phys_addr; >> >> crc32 = crc32_le(~0, addr, size); >> if (crc32 != pci_endpoint_test_readl(test, PCI_ENDPOINT_TEST_CHECKSUM)) >> @@ -1141,10 +1171,15 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, >> { >> int ret = -EINVAL; >> enum pci_barno bar; >> - struct pci_endpoint_test *test = to_endpoint_test(file->private_data); >> - struct pci_dev *pdev = test->pdev; >> + struct pci_endpoint_test *test = file->private_data; >> + struct pci_dev *pdev; >> >> mutex_lock(&test->mutex); >> + pdev = test->pdev; >> + if (!pdev) { >> + ret = -ENODEV; >> + goto ret; >> + } >> >> reinit_completion(&test->irq_raised); >> test->last_irq = -ENODATA; >> @@ -1206,9 +1241,30 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, >> return ret; >> } >> >> +static int pci_endpoint_test_open(struct inode *inode, struct file *file) >> +{ >> + struct miscdevice *miscdev = file->private_data; >> + struct pci_endpoint_test *test = to_endpoint_test(miscdev); >> + >> + kref_get(&test->kref); >> + file->private_data = test; >> + >> + return 0; >> +} >> + >> +static int pci_endpoint_test_release(struct inode *inode, struct file *file) >> +{ >> + struct pci_endpoint_test *test = file->private_data; >> + >> + kref_put(&test->kref, pci_endpoint_test_free); >> + return 0; >> +} >> + >> static const struct file_operations pci_endpoint_test_fops = { >> .owner = THIS_MODULE, >> + .open = pci_endpoint_test_open, >> .unlocked_ioctl = pci_endpoint_test_ioctl, >> + .release = pci_endpoint_test_release, >> }; >> >> static void pci_endpoint_test_get_capabilities(struct pci_endpoint_test *test) >> @@ -1241,10 +1297,11 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, >> if (pci_is_bridge(pdev)) >> return -ENODEV; >> >> - test = devm_kzalloc(dev, sizeof(*test), GFP_KERNEL); >> + test = kzalloc_obj(*test); >> if (!test) >> return -ENOMEM; >> >> + kref_init(&test->kref); >> test->pdev = pdev; >> test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; >> >> @@ -1263,7 +1320,7 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, >> ret = pci_enable_device(pdev); >> if (ret) { >> dev_err(dev, "Cannot enable PCI device\n"); >> - return ret; >> + goto err_kfree_test; >> } >> >> ret = pci_request_regions(pdev, DRV_MODULE_NAME); >> @@ -1349,6 +1406,9 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, >> err_disable_pdev: >> pci_disable_device(pdev); >> >> +err_kfree_test: >> + kfree(test); >> + >> return ret; >> } >> >> @@ -1364,10 +1424,13 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) >> if (id < 0) >> return; >> >> + misc_deregister(&test->miscdev); >> + >> + mutex_lock(&test->mutex); >> + >> pci_endpoint_test_release_irq(test); >> pci_endpoint_test_free_irq_vectors(test); >> >> - misc_deregister(&test->miscdev); >> kfree(misc_device->name); >> kfree(test->name); >> ida_free(&pci_endpoint_test_ida, id); >> @@ -1378,6 +1441,11 @@ static void pci_endpoint_test_remove(struct pci_dev *pdev) >> >> pci_release_regions(pdev); >> pci_disable_device(pdev); >> + pci_set_drvdata(pdev, NULL); >> + test->pdev = NULL; >> + mutex_unlock(&test->mutex); >> + >> + kref_put(&test->kref, pci_endpoint_test_free); >> } >> >> static const struct pci_endpoint_test_data default_data = { >> -- >> 2.34.1 >> ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-22 20:40 ` Frank Li 2026-06-24 4:07 ` Shuangpeng @ 2026-06-27 12:59 ` Manivannan Sadhasivam 2026-06-27 17:29 ` Greg KH 1 sibling, 1 reply; 11+ messages in thread From: Manivannan Sadhasivam @ 2026-06-27 12:59 UTC (permalink / raw) To: Frank Li Cc: Shuangpeng Bai, kwilczynski, kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel On Mon, Jun 22, 2026 at 03:40:43PM -0500, Frank Li wrote: > On Mon, Jun 22, 2026 at 12:52:49PM -0400, Shuangpeng Bai wrote: > > [You don't often get email from shuangpeng.kernel@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > > > An open miscdevice file descriptor can outlive the PCI driver binding. > > misc_deregister() removes the device node and prevents new opens, but it > > does not revoke file descriptors that are already open. > > > > Before this change, pci_endpoint_test stored the miscdevice inside struct > > pci_endpoint_test, and ioctl() recovered the test object from > > file->private_data with container_of(). Since the test object was allocated > > with devm_kzalloc(), it was freed when the PCI device was unbound. A > > process could therefore open /dev/pci-endpoint-test.N, unbind the PCI > > device through sysfs, and then issue an ioctl on the stale file descriptor, > > causing a use-after-free of struct pci_endpoint_test. > > Anyways to provent unbound when still open? This driver is only for test > ednpoint funciton, it is not worth to make complex for this unusual case. > I disagree here. Even though this is a simple test driver, this is exposing an IOCTL to userspace. So the driver should take all necessary steps to prevent security issues like UAF. - Mani -- மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-27 12:59 ` Manivannan Sadhasivam @ 2026-06-27 17:29 ` Greg KH 0 siblings, 0 replies; 11+ messages in thread From: Greg KH @ 2026-06-27 17:29 UTC (permalink / raw) To: Manivannan Sadhasivam Cc: Frank Li, Shuangpeng Bai, kwilczynski, kishon, Frank.Li, arnd, bhelgaas, linux-pci, linux-kernel On Sat, Jun 27, 2026 at 06:29:07PM +0530, Manivannan Sadhasivam wrote: > On Mon, Jun 22, 2026 at 03:40:43PM -0500, Frank Li wrote: > > On Mon, Jun 22, 2026 at 12:52:49PM -0400, Shuangpeng Bai wrote: > > > [You don't often get email from shuangpeng.kernel@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > > > > > An open miscdevice file descriptor can outlive the PCI driver binding. > > > misc_deregister() removes the device node and prevents new opens, but it > > > does not revoke file descriptors that are already open. > > > > > > Before this change, pci_endpoint_test stored the miscdevice inside struct > > > pci_endpoint_test, and ioctl() recovered the test object from > > > file->private_data with container_of(). Since the test object was allocated > > > with devm_kzalloc(), it was freed when the PCI device was unbound. A > > > process could therefore open /dev/pci-endpoint-test.N, unbind the PCI > > > device through sysfs, and then issue an ioctl on the stale file descriptor, > > > causing a use-after-free of struct pci_endpoint_test. > > > > Anyways to provent unbound when still open? This driver is only for test > > ednpoint funciton, it is not worth to make complex for this unusual case. > > > > I disagree here. Even though this is a simple test driver, this is exposing an > IOCTL to userspace. So the driver should take all necessary steps to prevent > security issues like UAF. That UAF can ONLY happen if root causes it to happen :) ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2] misc: pci_endpoint_test: fix use-after-free after device unbind 2026-06-22 16:52 ` [PATCH v2] " Shuangpeng Bai 2026-06-22 20:40 ` Frank Li @ 2026-06-27 13:16 ` Manivannan Sadhasivam 1 sibling, 0 replies; 11+ messages in thread From: Manivannan Sadhasivam @ 2026-06-27 13:16 UTC (permalink / raw) To: Shuangpeng Bai Cc: kwilczynski, kishon, Frank.Li, arnd, gregkh, bhelgaas, linux-pci, linux-kernel On Mon, Jun 22, 2026 at 12:52:49PM -0400, Shuangpeng Bai wrote: [...] > @@ -1141,10 +1171,15 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, > { > int ret = -EINVAL; > enum pci_barno bar; > - struct pci_endpoint_test *test = to_endpoint_test(file->private_data); > - struct pci_dev *pdev = test->pdev; > + struct pci_endpoint_test *test = file->private_data; Can't you still use to_endpoint_test()? > + struct pci_dev *pdev; > > mutex_lock(&test->mutex); > + pdev = test->pdev; > + if (!pdev) { > + ret = -ENODEV; > + goto ret; > + } > > reinit_completion(&test->irq_raised); > test->last_irq = -ENODATA; > @@ -1206,9 +1241,30 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, > return ret; > } > > +static int pci_endpoint_test_open(struct inode *inode, struct file *file) > +{ > + struct miscdevice *miscdev = file->private_data; > + struct pci_endpoint_test *test = to_endpoint_test(miscdev); > + > + kref_get(&test->kref); > + file->private_data = test; Same as above. This is not needed. Rest LGTM! - Mani -- மணிவண்ணன் சதாசிவம் ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-06-27 17:30 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-06-14 22:16 [BUG] KASAN: slab-use-after-free in pci_endpoint_test_ioctl Shuangpeng Bai 2026-06-15 2:39 ` Greg KH 2026-06-15 20:06 ` Shuangpeng 2026-06-18 3:01 ` Krzysztof Wilczyński 2026-06-21 5:46 ` [PATCH] misc: pci_endpoint_test: fix use-after-free after device unbind Shuangpeng Bai 2026-06-22 16:52 ` [PATCH v2] " Shuangpeng Bai 2026-06-22 20:40 ` Frank Li 2026-06-24 4:07 ` Shuangpeng 2026-06-27 12:59 ` Manivannan Sadhasivam 2026-06-27 17:29 ` Greg KH 2026-06-27 13:16 ` Manivannan Sadhasivam
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox