drivers/media/pci/b2c2/flexcop-pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
The original code uses cancel_delayed_work() in flexcop_pci_remove(), which
does not guarantee that the delayed work item irq_check_work has fully
completed if it was already running. This leads to use-after-free scenarios
where flexcop_pci_remove() may free the flexcop_device while irq_check_work
is still active and attempts to dereference the device.
A typical race condition is illustrated below:
CPU 0 (remove) | CPU 1 (delayed work callback)
flexcop_pci_remove() | flexcop_pci_irq_check_work()
cancel_delayed_work() |
flexcop_device_kfree(fc_pci->fc_dev) |
| fc = fc_pci->fc_dev; // UAF
This is confirmed by a KASAN report:
[ 60.718921] ==================================================================
[ 60.734503] BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x7d7/0x8c0
[ 60.737894] Write of size 8 at addr ffff8880093aa8c8 by task bash/135
...
[ 60.742475] Call Trace:
[ 60.742729] <IRQ>
[ 60.743030] dump_stack_lvl+0x55/0x70
[ 60.743256] print_report+0xcf/0x610
[ 60.743335] ? __run_timer_base.part.0+0x7d7/0x8c0
[ 60.743394] kasan_report+0xb8/0xf0
[ 60.743489] ? __run_timer_base.part.0+0x7d7/0x8c0
[ 60.743541] __run_timer_base.part.0+0x7d7/0x8c0
[ 60.743598] ? __pfx___run_timer_base.part.0+0x10/0x10
[ 60.743646] ? __pfx_read_tsc+0x10/0x10
[ 60.743684] ? ktime_get+0x60/0x140
[ 60.743725] ? lapic_next_event+0x11/0x20
[ 60.743764] ? clockevents_program_event+0x1d4/0x2a0
[ 60.743813] run_timer_softirq+0xd1/0x190
[ 60.743852] handle_softirqs+0x16a/0x550
[ 60.743911] irq_exit_rcu+0xaf/0xe0
[ 60.743948] sysvec_apic_timer_interrupt+0x70/0x80
[ 60.744044] </IRQ>
[ 60.744083] <TASK>
[ 60.744108] asm_sysvec_apic_timer_interrupt+0x1a/0x20
[ 60.744335] RIP: 0010:down_write+0x0/0x130
[ 60.744907] Code: 89 f7 e8 73 e6 e7 fc e9 bf f9 ff ff 66 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 90 90 908
[ 60.745823] RSP: 0018:ffff88800d86fc18 EFLAGS: 00010282
[ 60.747927] RAX: 0000000000000000 RBX: ffff8880095bc3e8 RCX: 0000000000000100
[ 60.748058] RDX: 1ffff11001ab2450 RSI: ffff88800a054c58 RDI: ffff88800837be68
[ 60.748169] RBP: ffff888009734640 R08: ffffffff8194c204 R09: ffff88806cb090b0
[ 60.748281] R10: 00000000000000a0 R11: ffff88800ceeeeb0 R12: ffff88800837be68
[ 60.748418] R13: ffff88800a054c58 R14: 0000000000000000 R15: ffffffff81b75f80
[ 60.748534] ? __pfx_sysfs_kf_write+0x10/0x10
[ 60.748606] ? kasan_save_stack+0x24/0x50
[ 60.748728] kernfs_remove_by_name_ns+0x81/0x110
[ 60.748779] pci_remove_resource_files+0x48/0xb0
[ 60.748834] pci_stop_and_remove_bus_device_locked+0x15/0x30
[ 60.748878] remove_store+0xcc/0xe0
[ 60.748929] ? __pfx_remove_store+0x10/0x10
[ 60.748999] ? sysfs_kf_write+0xad/0x1a0
[ 60.749044] kernfs_fop_write_iter+0x2c3/0x440
[ 60.749091] vfs_write+0x871/0xd70
[ 60.749143] ? __pfx_vfs_write+0x10/0x10
[ 60.749204] ? fdget_pos+0x1c8/0x4c0
[ 60.749243] ? lapic_next_event+0x11/0x20
[ 60.749278] ? clockevents_program_event+0x1d4/0x2a0
[ 60.749322] ksys_write+0xee/0x1c0
[ 60.749361] ? __pfx_ksys_write+0x10/0x10
[ 60.749414] do_syscall_64+0xac/0x280
[ 60.749460] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 60.749499] RIP: 0033:0x7fbe1c04cad3
[ 60.750304] Code: 0c 00 f7 d8 64 89 02 48 c7 c0 ff ff ff ff eb bb 0f 1f 80 00 00 00 00 64 8b 04 25 18 008
[ 60.750348] RSP: 002b:00007ffdb3c76078 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
[ 60.750737] RAX: ffffffffffffffda RBX: 0000000000000002 RCX: 00007fbe1c04cad3
[ 60.750782] RDX: 0000000000000002 RSI: 00000000010467c0 RDI: 0000000000000001
[ 60.750816] RBP: 00000000010467c0 R08: 000000000000000a R09: 0000000000000001
[ 60.750841] R10: 000000000041204f R11: 0000000000000246 R12: 0000000000000002
[ 60.750867] R13: 00007fbe1c11c6a0 R14: 0000000000000002 R15: 00007fbe1c1178a0
[ 60.750924] </TASK>
[ 60.751106]
[ 60.763864] Allocated by task 1:
[ 60.764506] kasan_save_stack+0x24/0x50
[ 60.765396] kasan_save_track+0x14/0x30
[ 60.765858] __kasan_kmalloc+0x7f/0x90
[ 60.766098] __kmalloc_noprof+0x1be/0x460
[ 60.766452] flexcop_device_kmalloc+0x54/0xe0
[ 60.766698] flexcop_pci_probe+0x1f/0x9d0
[ 60.767020] local_pci_probe+0xdc/0x190
[ 60.767283] pci_device_probe+0x2fe/0x470
[ 60.767499] really_probe+0x1ca/0x5c0
[ 60.767729] __driver_probe_device+0x248/0x310
[ 60.767953] driver_probe_device+0x44/0x120
[ 60.768262] __driver_attach+0xd2/0x310
[ 60.768457] bus_for_each_dev+0xed/0x170
[ 60.768629] bus_add_driver+0x208/0x500
[ 60.768829] driver_register+0x132/0x460
[ 60.769077] do_one_initcall+0x89/0x300
[ 60.769361] kernel_init_freeable+0x40d/0x720
[ 60.769623] kernel_init+0x1a/0x150
[ 60.769847] ret_from_fork+0x10c/0x1a0
[ 60.770139] ret_from_fork_asm+0x1a/0x30
[ 60.770480]
[ 60.770674] Freed by task 135:
[ 60.770890] kasan_save_stack+0x24/0x50
[ 60.771135] kasan_save_track+0x14/0x30
[ 60.771354] kasan_save_free_info+0x3a/0x60
[ 60.771581] __kasan_slab_free+0x3f/0x50
[ 60.771824] kfree+0x137/0x370
[ 60.772023] flexcop_device_kfree+0x32/0x50
[ 60.772254] pci_device_remove+0xa6/0x1d0
[ 60.772492] device_release_driver_internal+0xf8/0x210
[ 60.772774] pci_stop_bus_device+0x105/0x150
[ 60.772992] pci_stop_and_remove_bus_device_locked+0x15/0x30
[ 60.773272] remove_store+0xcc/0xe0
[ 60.773473] kernfs_fop_write_iter+0x2c3/0x440
[ 60.773731] vfs_write+0x871/0xd70
[ 60.773933] ksys_write+0xee/0x1c0
[ 60.774151] do_syscall_64+0xac/0x280
[ 60.774382] entry_SYSCALL_64_after_hwframe+0x77/0x7f
...
Replace cancel_delayed_work() with cancel_delayed_work_sync() to ensure
that the delayed work item is properly canceled and any executing delayed
work has finished before the device memory is deallocated.
Fixes: 382c5546d618 ("V4L/DVB (10694): [PATCH] software IRQ watchdog for Flexcop B2C2 DVB PCI cards")
Signed-off-by: Duoming Zhou <duoming@zju.edu.cn>
---
drivers/media/pci/b2c2/flexcop-pci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/pci/b2c2/flexcop-pci.c b/drivers/media/pci/b2c2/flexcop-pci.c
index 486c8ec0fa60..ab53c5b02c48 100644
--- a/drivers/media/pci/b2c2/flexcop-pci.c
+++ b/drivers/media/pci/b2c2/flexcop-pci.c
@@ -411,7 +411,7 @@ static void flexcop_pci_remove(struct pci_dev *pdev)
struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
if (irq_chk_intv > 0)
- cancel_delayed_work(&fc_pci->irq_check_work);
+ cancel_delayed_work_sync(&fc_pci->irq_check_work);
flexcop_pci_dma_exit(fc_pci);
flexcop_device_exit(fc_pci->fc_dev);
--
2.34.1
© 2016 - 2025 Red Hat, Inc.