* [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs
@ 2026-10-24 16:15 Cen Zhang
2026-11-10 11:03 ` Chad Aldreda
2026-11-10 16:10 ` patchwork-bot+bluetooth
0 siblings, 2 replies; 3+ messages in thread
From: Cen Zhang @ 2026-10-24 16:15 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming, zzzccc427
The VHCI controller must remain alive until suspend_work finishes.
vhci_release() flushes the work before removing the force_suspend
debugfs entry, so an active writer can queue work after that flush.
A force_suspend write changing the suspend state can overlap the last
close of /dev/vhci as follows:
/dev/vhci close Debugfs writer Suspend worker
--------------- -------------- --------------
vhci_release() force_suspend_write()
flush_work() returns [not queued yet]
schedule_work() -->
hci_suspend_dev()
check powered
check !HCI_UNREGISTER
hci_suspend_sync()
unlock(req_lock)
vhci_debugfs_remove()
wait for writer
hci_unregister_dev()
hci_free_dev()
drop last reference
free hdev
hci_clear_wake_reason()
lock freed mutex
use-after-free
The controller locks serialize state access but do not retain the
allocation. Unregister drains HCI-owned work, but does not cover VHCI's
suspend_work on the system workqueue.
Flush suspend_work again after removing the debugfs entries and before
unregistering the controller. Removal waits for active writers and
prevents new callbacks, so the second flush drains all remaining work
while the controller and driver data are still alive. Retain the initial
flush to preserve the existing teardown order.
Runtime testing of this change was not performed.
KASAN report as below:
==================================================================
BUG: KASAN: use-after-free in __mutex_lock+0x1e68/0x2590
Read of size 8 at addr ffff8881011b80b0 by task kworker/0:0/9
Workqueue: events vhci_suspend_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
? __mutex_lock+0x1e68/0x2590
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0x20d/0x410
? __mutex_lock+0x1e68/0x2590
kasan_report+0xe0/0x110
? __mutex_lock+0x1e68/0x2590
__mutex_lock+0x1e68/0x2590
? hci_suspend_dev+0x1ff/0x650
? srso_alias_return_thunk+0x5/0xfbef5
? mark_held_locks+0x40/0x70
? _raw_spin_unlock_irqrestore+0x44/0x60
? srso_alias_return_thunk+0x5/0xfbef5
? lockdep_hardirqs_on_prepare+0xea/0x1a0
? srso_alias_return_thunk+0x5/0xfbef5
? __pfx___mutex_lock+0x10/0x10
? _raw_spin_unlock_irqrestore+0x44/0x60
? srso_alias_return_thunk+0x5/0xfbef5
? pmbd_gate_site_armed+0x31/0x1c0
? srso_alias_return_thunk+0x5/0xfbef5
? __pfx_pmbd_probe_hit_cookie+0x10/0x10
? hci_suspend_dev+0x1ff/0x650
hci_suspend_dev+0x1ff/0x650
vhci_suspend_work+0xa5/0x190
process_one_work+0x908/0x19c0
? __pfx_process_one_work+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? lock_is_held_type+0x8f/0x100
? srso_alias_return_thunk+0x5/0xfbef5
worker_thread+0x65c/0xe40
? __pfx_worker_thread+0x10/0x10
kthread+0x34f/0x460
? srso_alias_return_thunk+0x5/0xfbef5
? __pfx_kthread+0x10/0x10
ret_from_fork+0x659/0x940
? __pfx_ret_from_fork+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? __switch_to+0x74f/0xf80
? __pfx_kthread+0x10/0x10
ret_from_fork_asm+0x1a/0x30
</TASK>
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000
index:0xffff8881011b9040 pfn:0x1011b8
flags: 0x200000000000000(node=0|zone=2)
raw: 0200000000000000 ffffea0004073308 ffff88818863f180
0000000000000000
raw: ffff8881011b9040 0000000000000000 00000000ffffffff
0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8881011b7f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ffff8881011b8000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
>ffff8881011b8080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
^
ffff8881011b8100: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ffff8881011b8180: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
==================================================================
Fixes: d16e6d19ccc6 ("Bluetooth: hci_vhci: Fix calling hci_{suspend,resume}_dev")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index 8f7db0102b25f..bedba3c140ac2 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -701,6 +701,8 @@ static int vhci_release(struct inode *inode, struct file *file)
if (hdev) {
if (!IS_ERR_OR_NULL(hdev->debugfs))
vhci_debugfs_remove(hdev);
+ /* Drain work queued by the last force_suspend writers. */
+ flush_work(&data->suspend_work);
hci_unregister_dev(hdev);
hci_free_dev(hdev);
}
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs 2026-10-24 16:15 [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs Cen Zhang @ 2026-11-10 11:03 ` Chad Aldreda 2026-11-10 16:10 ` patchwork-bot+bluetooth 1 sibling, 0 replies; 3+ messages in thread From: Chad Aldreda @ 2026-11-10 11:03 UTC (permalink / raw) To: Cen Zhang, Marcel Holtmann, Luiz Augusto von Dentz Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming On Sun, 25 Oct 2026, Cen Zhang wrote: > Runtime testing of this change was not performed. The use-after-free in your report reproduces on mainline with the two test-only delays below, and your patch removes it. Tested on mainline d48723711bae (v7.4-rc1-672), whose hci_vhci.c and hci_sync.c match bluetooth-next at 983f3fcb1282 (hci_core.c differs only in rfkill handling), on x86_64 in QEMU (4 vCPUs) with KASAN, lockdep, DEBUG_LIST, kmemleak and hung-task detection. A test program opens /dev/vhci, answers every HCI command with a Command Complete, brings the controller up with HCIDEVUP, then closes /dev/vhci while another thread keeps toggling force_suspend in debugfs. One mode keeps answering commands until close() returns; the other stops answering before close(). Unaided the window is too narrow: 300 cycles in the answering mode on the unpatched kernel gave no reports. Both kernels therefore carried two test-only delays, not for merging: vhci_release(): cancel_delayed_work_sync(&data->open_timeout); flush_work(&data->suspend_work); + msleep(50); hci_suspend_dev(): hci_req_sync_unlock(hdev); + msleep(200); hci_clear_wake_reason(hdev); With the delays and without your patch, 30 cycles in each mode gave 834 and 621 KASAN reports (kasan_multi_shot, so one bad close gives many). Two objects are hit. The hci_dev is accessed after free from hci_suspend_dev(), including the __mutex_lock() site in your report; it is page-backed, so KASAN shows no free stack, and once the page is reused the same accesses show up as slab-out-of-bounds against unrelated slab objects. The vhci_data (kmalloc in vhci_open(), kfree in vhci_release()) is read by vhci_suspend_work() and read and written by process_one_work() after free, because suspend_work is embedded in it. hci_resume_dev() was also reached, with an hdev pointer read from the freed vhci_data, so it dereferenced an unrelated address. With the delays and your patch, 160 cycles (130 answering, 30 not) had no KASAN, lockdep, DEBUG_LIST, kmemleak or hung-task reports. Without the delays, 150 cycles with the patch (answering mode) were also clean; since the unpatched kernel was clean without them too, that run only shows no regression. As expected, release can now also wait for suspend or resume work that is already running, including an HCI command timeout inside it, as the existing first flush_work() already can. Not tested: suspend with force_wakeup set, with connections, or with scanning, advertising or discovery active (the controller was brought up with HCIDEVUP and no bluetoothd, so hci_suspend_sync() had nothing to pause), mgmt, real controllers or btvirt, module unload (Bluetooth was built in), other architectures, and a build of bluetooth-next itself (compared at source level only). The harness and this report were produced with an AI assistant (Claude Opus 5.5), which also did the earlier runs, including the unpatched comparison. I ran the final version on the patched kernel myself and got the same results. The harness is available on request. Tested-by: Chad Aldreda <ca@modelmiser.com> ^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs 2026-10-24 16:15 [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs Cen Zhang 2026-11-10 11:03 ` Chad Aldreda @ 2026-11-10 16:10 ` patchwork-bot+bluetooth 1 sibling, 0 replies; 3+ messages in thread From: patchwork-bot+bluetooth @ 2026-11-10 16:10 UTC (permalink / raw) To: Cen Zhang Cc: marcel, luiz.dentz, linux-bluetooth, linux-kernel, baijiaju1990, jjzuming Hello: This patch was applied to bluetooth/bluetooth-next.git (master) by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>: On Sun, 25 Oct 2026 00:15:17 +0800 you wrote: > The VHCI controller must remain alive until suspend_work finishes. > vhci_release() flushes the work before removing the force_suspend > debugfs entry, so an active writer can queue work after that flush. > > A force_suspend write changing the suspend state can overlap the last > close of /dev/vhci as follows: > > [...] Here is the summary with links: - Bluetooth: vhci: Flush suspend work after removing debugfs https://git.kernel.org/bluetooth/bluetooth-next/c/58d8a0955065 You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-10 16:10 UTC | newest] Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-24 16:15 [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs Cen Zhang 2026-11-10 11:03 ` Chad Aldreda 2026-11-10 16:10 ` patchwork-bot+bluetooth
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®