* [PATCH] Bluetooth: hci_sync: Copy the LE accept list under hdev->lock
@ 2026-10-22 12:16 Cen Zhang
0 siblings, 0 replies; only message in thread
From: Cen Zhang @ 2026-10-22 12:16 UTC (permalink / raw)
To: marcel, luiz.dentz
Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming, zzzccc427
The LE accept-list mirror consists of bdaddr_list entries that are
added, removed and freed under hdev->lock. The ordinary branch of
hci_update_accept_list_sync() traverses this list without that lock.
The request worker's req_lock does not serialize raw HCI completions,
and the RCU sections protect connection and pending-action lookups,
not the accept-list nodes.
Changing connectability can reach this iterator through
set_connectable_sync(), hci_update_connectable_sync(),
hci_update_passive_scan_sync() and hci_passive_scan_sync(). With a
populated mirror, a privileged raw HCI sender can issue
HCI_OP_LE_CLEAR_ACCEPT_LIST while request work is reconciling it:
Request work HCI event work
select b and successor t
hci_cc_le_clear_accept_list()
hci_dev_lock(hdev)
hci_bdaddr_list_clear()
unlink nodes and kfree(b)
hci_dev_unlock(hdev)
read b->bdaddr_type
The clear completion can therefore free the selected node before its
field read, or the saved successor before the iterator advances.
KASAN reported a one-byte read of bdaddr_type from the freed node.
Copy each entry's address and type under hdev->lock and reconcile the
private array instead. Release the mutex before issuing synchronous
commands, since their completion handlers also acquire it. Free the
copy after iteration and use the existing error path to disable
accept-list filtering if allocation fails.
This change has not been runtime-tested.
KASAN report as below:
==================================================================
BUG: KASAN: slab-use-after-free in hci_passive_scan_sync+0x1cc4/0x1f80
Read of size 1 at addr ffff888105ee6c56 by task kworker/u17:1/499
Workqueue: hci0 hci_cmd_sync_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
? hci_passive_scan_sync+0x1cc4/0x1f80
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0x20d/0x410
? hci_passive_scan_sync+0x1cc4/0x1f80
kasan_report+0xe0/0x110
? hci_passive_scan_sync+0x1cc4/0x1f80
hci_passive_scan_sync+0x1cc4/0x1f80
? pmbd_gate_site_armed+0x31/0x1c0
? __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/0x9e0
? srso_alias_return_thunk+0x5/0xfbef5
? lock_release+0xc8/0x280
hci_update_passive_scan_sync+0x473/0x9e0
hci_update_connectable_sync+0x13c/0x250
set_connectable_sync+0x39/0xb0
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 511:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_cache_noprof+0x251/0x610
hci_bdaddr_list_add+0x179/0x390
hci_cc_le_add_to_accept_list+0x189/0x2d0
hci_cmd_complete_evt+0x373/0xab0
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 511:
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+0x80/0x1f0
hci_cmd_complete_evt+0x373/0xab0
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 ffff888105ee6c40
which
belongs to the cache kmalloc-32 of size 32
The buggy address is located 22 bytes inside of
freed
32-byte region [ffff888105ee6c40,
ffff888105ee6c60)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000
index:0xffff888105ee69c0 pfn:0x105ee6
flags: 0x200000000000200(workingset|node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000200 ffff888100042780 ffffea0004187150
ffffea00042534d0
raw: ffff888105ee69c0 000000000040003f 00000000f5000000
0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff888105ee6b00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
ffff888105ee6b80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
>ffff888105ee6c00: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
^
ffff888105ee6c80: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
ffff888105ee6d00: 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>
---
Sashiko review follow-up: earlier accept-list patch
- Address the outer-iterator lifetime issue raised by Sashiko:
https://sashiko.dev/#/patchset/88963
- Copy accept-list addresses before issuing synchronous commands.
Related accept-list membership fix:
https://lore.kernel.org/r/pm-bluetooth-objects-candidate-0118-v7-e6b713327ef914d6d02a@gmail.com
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
--- a/net/bluetooth/hci_sync.c
+++ b/net/bluetooth/hci_sync.c
@@ -2872,6 +2872,31 @@
*n = i;
return p;
+}
+
+static struct conn_params *le_accept_list_copy(struct hci_dev *hdev,
+ size_t *n)
+{
+ struct bdaddr_list *entry;
+ struct conn_params *params;
+ size_t i = 0;
+
+ hci_dev_lock(hdev);
+
+ *n = list_count_nodes(&hdev->le_accept_list);
+ params = kvzalloc_objs(struct conn_params, *n);
+ if (!params)
+ goto unlock;
+
+ list_for_each_entry(entry, &hdev->le_accept_list, list) {
+ bacpy(¶ms[i].addr, &entry->bdaddr);
+ params[i].addr_type = entry->bdaddr_type;
+ i++;
+ }
+
+unlock:
+ hci_dev_unlock(hdev);
+ return params;
}
/* Clear LE Accept List */
@@ -2904,7 +2929,6 @@
static u8 hci_update_accept_list_sync(struct hci_dev *hdev)
{
struct conn_params *params;
- struct bdaddr_list *b, *t;
u8 num_entries = 0;
bool pend_conn, pend_report;
u8 filter_policy;
@@ -2969,20 +2993,27 @@
* report. If not present in either list, then remove it from
* the controller.
*/
- list_for_each_entry_safe(b, t, &hdev->le_accept_list, list) {
+ params = le_accept_list_copy(hdev, &n);
+ if (!params) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < n; i++) {
rcu_read_lock();
- if (hci_conn_hash_lookup_le(hdev, &b->bdaddr, b->bdaddr_type)) {
+ if (hci_conn_hash_lookup_le(hdev, ¶ms[i].addr,
+ params[i].addr_type)) {
rcu_read_unlock();
continue;
}
pend_conn = hci_pend_le_action_lookup(&hdev->pend_le_conns,
- &b->bdaddr,
- b->bdaddr_type);
- pend_report = hci_pend_le_action_lookup(&hdev->pend_le_reports,
- &b->bdaddr,
- b->bdaddr_type);
+ ¶ms[i].addr,
+ params[i].addr_type);
+ pend_report = hci_pend_le_action_lookup(
+ &hdev->pend_le_reports, ¶ms[i].addr,
+ params[i].addr_type);
rcu_read_unlock();
@@ -2990,13 +3021,15 @@
* remove it from the acceptlist.
*/
if (!pend_conn && !pend_report) {
- hci_le_del_accept_list_sync(hdev, &b->bdaddr,
- b->bdaddr_type);
+ hci_le_del_accept_list_sync(hdev, ¶ms[i].addr,
+ params[i].addr_type);
continue;
}
num_entries++;
}
+
+ kvfree(params);
/* Since all no longer valid accept list entries have been
* removed, walk through the list of pending connections
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-22 12:16 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 12:16 [PATCH] Bluetooth: hci_sync: Copy the LE accept list under hdev->lock Cen Zhang
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®