From: Cen Zhang <zzzccc427@gmail.com>
To: marcel@holtmann.org, luiz.dentz@gmail.com
Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH] Bluetooth: vhci: Flush suspend work after removing debugfs
Date: Sun, 25 Oct 2026 00:15:17 +0800 [thread overview]
Message-ID: <pm-bluetooth-objects-candidate-0146-v7-4a901c7fb65111f006fe@gmail.com> (raw)
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);
}
reply other threads:[~2026-10-24 16:15 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=pm-bluetooth-objects-candidate-0146-v7-4a901c7fb65111f006fe@gmail.com \
--to=zzzccc427@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=jjzuming@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=marcel@holtmann.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 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®