* [PATCH] Bluetooth: hci_sync: Protect accept list membership lookups
@ 2026-10-13 16:27 Cen Zhang
0 siblings, 0 replies; only message in thread
From: Cen Zhang @ 2026-10-13 16:27 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming, zzzccc427
Entries in hdev->le_accept_list must remain allocated while a reader
compares their address and type. hci_le_del_accept_list_sync() and
hci_le_add_accept_list_sync() call hci_bdaddr_list_lookup() without
hdev->lock, although command-complete handlers modify and free entries
under that mutex. The request worker's req_lock does not serialize raw
HCI commands or their completions.
With a nonempty accept list and scanning disabled for passive-scan
reconciliation, a successful raw LE Clear Accept List completion can
overlap a membership check. For example, removing the last pending peer
A stops scanning but leaves A in the accept list. Adding a new pending
peer B then reaches deletion of stale A:
1. The request worker holds req_lock, enters
hci_le_del_accept_list_sync() and obtains entry A during
hci_bdaddr_list_lookup(), without hdev->lock.
2. A CAP_NET_RAW sender submits LE Clear Accept List through a raw HCI
socket. The event worker handles its successful completion in
hci_cc_le_clear_accept_list(), takes hdev->lock, then unlinks and
frees A through hci_bdaddr_list_clear() and releases hdev->lock.
3. After that free, the request worker compares A's bdaddr_type.
The final comparison reads freed memory; KASAN reported a one-byte read
22 bytes into the bdaddr_list allocation. Hold hdev->lock across both
membership checks so the clear completion cannot free an entry during
the traversal. Keep the delete command's address copy under that lock
as well, because its caller can pass an address embedded in the list
entry. Release the mutex before resolving-list operations or synchronous
HCI commands, whose completion handlers also acquire it.
KASAN report as below:
BUG: KASAN: slab-use-after-free in hci_bdaddr_list_lookup+0x107/0x120
Read of size 1 at addr ffff8881058e8c56 by task kworker/u17:0/501
Workqueue: hci0 hci_cmd_sync_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
? hci_bdaddr_list_lookup+0x107/0x120
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0x20d/0x410
? hci_bdaddr_list_lookup+0x107/0x120
kasan_report+0xe0/0x110
? hci_bdaddr_list_lookup+0x107/0x120
hci_bdaddr_list_lookup+0x107/0x120
hci_le_del_accept_list_sync.isra.0+0xa1/0x310
? __pfx_hci_le_del_accept_list_sync.isra.0+0x10/0x10
? hci_passive_scan_sync+0xd88/0x2090
? srso_alias_return_thunk+0x5/0xfbef5
? lock_release+0xc8/0x280
hci_passive_scan_sync+0x10a7/0x2090
? __pfx_hci_passive_scan_sync+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? pmbd_probe_hit_cookie+0x10/0x1c0
? __pfx_pmbd_probe_hit_cookie+0x10/0x10
? hci_update_passive_scan_sync+0x43f/0x9f0
? srso_alias_return_thunk+0x5/0xfbef5
? lock_release+0xc8/0x280
hci_update_passive_scan_sync+0x473/0x9f0
hci_cmd_sync_work+0x1b3/0x450
? srso_alias_return_thunk+0x5/0xfbef5
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>
Allocated by task 504:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_cache_noprof+0x251/0x610
hci_bdaddr_list_add+0xdf/0x2c0
hci_cc_le_add_to_accept_list+0x17a/0x230
hci_cmd_complete_evt+0x37d/0xb60
hci_event_packet+0x4e8/0xc70
hci_rx_work+0x367/0xf00
process_one_work+0x908/0x19c0
worker_thread+0x65c/0xe40
kthread+0x34f/0x460
ret_from_fork+0x659/0x940
ret_from_fork_asm+0x1a/0x30
Freed by task 504:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x5f/0x80
kfree+0x307/0x580
hci_bdaddr_list_clear+0x134/0x230
hci_cc_le_clear_accept_list+0x13d/0x240
hci_cmd_complete_evt+0x37d/0xb60
hci_event_packet+0x4e8/0xc70
hci_rx_work+0x367/0xf00
process_one_work+0x908/0x19c0
worker_thread+0x65c/0xe40
kthread+0x34f/0x460
ret_from_fork+0x659/0x940
ret_from_fork_asm+0x1a/0x30
The buggy address belongs to the object at ffff8881058e8c40
which
belongs to the cache kmalloc-32 of size 32
The buggy address is located 22 bytes inside of
freed
32-byte region [ffff8881058e8c40,
ffff8881058e8c60)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0x1058e8
flags: 0x200000000000000(node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000000 ffff888100042780 dead000000000100
dead000000000122
raw: 0000000000000000 0000000000400040 00000000f5000000
0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8881058e8b00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
ffff8881058e8b80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
>ffff8881058e8c00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
^
ffff8881058e8c80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
ffff8881058e8d00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
==================================================================
Fixes: e8907f76544f ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 3")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
index efbd2e5387c0b..4e41c78a336b8 100644
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -2473,11 +2473,16 @@ static int hci_le_del_accept_list_sync(struct hci_dev *hdev,
int err;
/* Check if device is on accept list before removing it */
- if (!hci_bdaddr_list_lookup(&hdev->le_accept_list, bdaddr, bdaddr_type))
+ hci_dev_lock(hdev);
+ if (!hci_bdaddr_list_lookup(&hdev->le_accept_list, bdaddr,
+ bdaddr_type)) {
+ hci_dev_unlock(hdev);
return 0;
+ }
cp.bdaddr_type = bdaddr_type;
bacpy(&cp.bdaddr, bdaddr);
+ hci_dev_unlock(hdev);
/* Ignore errors when removing from resolving list as that is likely
* that the device was never added.
@@ -2653,9 +2658,13 @@ static int hci_le_add_accept_list_sync(struct hci_dev *hdev,
}
/* Check if already in accept list */
+ hci_dev_lock(hdev);
if (hci_bdaddr_list_lookup(&hdev->le_accept_list, ¶ms->addr,
- params->addr_type))
+ params->addr_type)) {
+ hci_dev_unlock(hdev);
return 0;
+ }
+ hci_dev_unlock(hdev);
*num_entries += 1;
cp.bdaddr_type = params->addr_type;
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-13 16:27 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 16:27 [PATCH] Bluetooth: hci_sync: Protect accept list membership lookups Cen Zhang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox