From: Cen Zhang <zzzccc427@gmail.com>
To: marcel@holtmann.org, luiz.dentz@gmail.com, johan.hedberg@intel.com
Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH] Bluetooth: hci_core: Keep link keys alive during updates
Date: Mon, 12 Oct 2026 10:57:29 +0800 [thread overview]
Message-ID: <pm-bluetooth-objects-candidate-0189-v6-def2692b4feebc514f3f@gmail.com> (raw)
Link keys must remain allocated until hci_add_link_key() finishes
reading the old type and writing the new fields. hci_find_link_key()
returns a borrowed pointer after dropping its RCU read lock, and a
new key is linked before its fields are filled in.
When LE Secure Connections pairing derives a BR/EDR link key,
smp_notify_keys() calls hci_add_link_key() without hdev->lock. The
L2CAP connection and channel locks held on this path do not exclude
the device lock taken by a concurrent MGMT Load Link Keys command.
The following ordering is possible:
SMP receive worker MGMT / RCU callback
------------------ -------------------
hci_add_link_key():
select or publish K
load_link_keys(): lock hdev
hci_link_keys_clear():
unlink K
queue kfree_rcu(K, rcu)
unlock hdev
grace period ends; free K
write fields in K
Removal and the grace period can complete after selection or
publication but before the remaining stores. This writes to freed
memory; KASAN reported a six-byte address-field write in
hci_add_link_key().
Hold an outer RCU read-side section across lookup and the last helper
access to the key. On a lookup miss, drop it for the sleeping
allocation and reacquire it before publishing the private new key.
Removal may still unlink the node, but its deferred free must wait
until the helper has finished updating it. This preserves the existing
allocation flags and avoids reversing the device-to-L2CAP lock order.
KASAN report as below:
BUG: KASAN: slab-use-after-free in hci_add_link_key+0x772/0x970
Write of size 6 at addr ffff8881074b8120 by task kworker/u17:1/502
Workqueue: hci0 hci_rx_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
? hci_add_link_key+0x772/0x970
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0x20d/0x410
? hci_add_link_key+0x772/0x970
kasan_report+0xe0/0x110
? hci_add_link_key+0x772/0x970
kasan_check_range+0x105/0x1b0
__asan_memcpy+0x3c/0x60
hci_add_link_key+0x772/0x970
smp_distribute_keys+0xaf6/0x2800
? __pfx_smp_distribute_keys+0x10/0x10
? kasan_save_track+0x14/0x30
? srso_alias_return_thunk+0x5/0xfbef5
? __kasan_kmalloc+0xaa/0xb0
? smp_recv_cb+0x4d32/0x8d80
? srso_alias_return_thunk+0x5/0xfbef5
? __kmalloc_cache_noprof+0x251/0x610
? smp_recv_cb+0x4d32/0x8d80
? smp_recv_cb+0x4e25/0x8d80
? srso_alias_return_thunk+0x5/0xfbef5
smp_recv_cb+0x4e25/0x8d80
? __pfx_smp_recv_cb+0x10/0x10
? __pfx___might_resched+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? stack_depot_save_flags+0x29/0x9a0
? srso_alias_return_thunk+0x5/0xfbef5
? __mutex_lock+0x182c/0x2590
? kasan_save_stack+0x42/0x60
? l2cap_recv_frame+0x9ef/0x8830
? __kasan_slab_free+0x5f/0x80
? kmem_cache_free+0x242/0x580
? kfree_skbmem+0x15e/0x1d0
? sk_skb_reason_drop+0x12f/0x490
? hci_event_packet+0x556/0xc70
? hci_rx_work+0x367/0xf00
? process_one_work+0x908/0x19c0
? worker_thread+0x65c/0xe40
? __pfx___mutex_lock+0x10/0x10
? __lock_acquire+0x466/0x2260
? __pfx_l2cap_chan_hold_unless_zero+0x10/0x10
? l2cap_recv_frame+0x13cc/0x8830
? __pfx_smp_recv_cb+0x10/0x10
l2cap_recv_frame+0x13cc/0x8830
? srso_alias_return_thunk+0x5/0xfbef5
? srso_alias_return_thunk+0x5/0xfbef5
? __mutex_lock+0x182c/0x2590
? __pfx_l2cap_recv_frame+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? find_held_lock+0x2b/0x80
? l2cap_recv_acldata+0x1eb/0x10e0
? __pfx___mutex_lock+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? __mutex_unlock_slowpath+0x147/0x920
? __pfx_l2cap_conn_hold_unless_zero+0x10/0x10
? __pfx_hci_conn_enter_active_mode+0x10/0x10
? l2cap_recv_acldata+0x13d/0x10e0
? srso_alias_return_thunk+0x5/0xfbef5
l2cap_recv_acldata+0xe9f/0x10e0
? srso_alias_return_thunk+0x5/0xfbef5
hci_rx_work+0x5a6/0xf00
? 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 502:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_cache_noprof+0x251/0x610
hci_add_link_key+0x5b8/0x970
smp_distribute_keys+0xaf6/0x2800
smp_recv_cb+0x4e25/0x8d80
l2cap_recv_frame+0x13cc/0x8830
l2cap_recv_acldata+0xe9f/0x10e0
hci_rx_work+0x5a6/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 0:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x5f/0x80
__rcu_free_sheaf_prepare+0x65/0x2a0
rcu_free_sheaf+0x1f/0xe0
rcu_core+0x661/0x1d10
handle_softirqs+0x201/0x930
__irq_exit_rcu+0x110/0x1e0
irq_exit_rcu+0xe/0x20
sysvec_apic_timer_interrupt+0x6c/0x80
asm_sysvec_apic_timer_interrupt+0x1a/0x20
The buggy address belongs to the object at ffff8881074b8100
which
belongs to the cache kmalloc-64 of size 64
The buggy address is located 32 bytes inside of
freed
64-byte region [ffff8881074b8100,
ffff8881074b8140)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0x1074b8
flags: 0x200000000000000(node=0|zone=2)
page_type: f5(slab)
raw: 0200000000000000 ffff8881000428c0 dead000000000100
dead000000000122
raw: 0000000000000000 0000000000200020 00000000f5000000
0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff8881074b8000: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff8881074b8080: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>ffff8881074b8100: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
^
ffff8881074b8180: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff8881074b8200: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
==================================================================
Fixes: 0378b5977013 ("Bluetooth: Convert link keys list to use RCU")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 209244fd93918..272812ee3d5ae 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1243,15 +1243,21 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
struct link_key *key, *old_key;
u8 old_key_type;
+ /* Keep the key alive until all field updates are complete. */
+ rcu_read_lock();
old_key = hci_find_link_key(hdev, bdaddr);
if (old_key) {
old_key_type = old_key->type;
key = old_key;
} else {
+ rcu_read_unlock();
+
old_key_type = conn ? conn->key_type : 0xff;
key = kzalloc_obj(*key);
if (!key)
return NULL;
+
+ rcu_read_lock();
list_add_rcu(&key->list, &hdev->link_keys);
}
@@ -1280,6 +1286,8 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn,
*persistent = hci_persistent_key(hdev, conn, type,
old_key_type);
+ rcu_read_unlock();
+
return key;
}
reply other threads:[~2026-10-12 2:57 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-0189-v6-def2692b4feebc514f3f@gmail.com \
--to=zzzccc427@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=jjzuming@gmail.com \
--cc=johan.hedberg@intel.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®