* [PATCH] Bluetooth: Keep IRKs alive during updates
@ 2026-10-12 3:02 Cen Zhang
0 siblings, 0 replies; only message in thread
From: Cen Zhang @ 2026-10-12 3:02 UTC (permalink / raw)
To: marcel, luiz.dentz, johan.hedberg
Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming, zzzccc427
An IRK selected by hci_add_irk() must remain allocated until its value
and RPA have been copied. hci_find_irk_by_addr() returns a borrowed
pointer after dropping its RCU read lock, leaving the subsequent stores
without lifetime protection on the SMP receive path.
A valid SMP Identity Address Information message can update an existing
IRK while MGMT Load IRKs replaces the identity resolving key list.
l2cap_recv_acldata() drops hdev->lock before SMP processing, so the
connection and channel locks do not exclude load_irks(). One possible
ordering is:
SMP receive worker Management / RCU
hci_add_irk()
hci_find_irk_by_addr()
rcu_read_lock()
find matching IRK
rcu_read_unlock()
load_irks()
hci_dev_lock()
hci_smp_irks_clear()
list_del_rcu()
kfree_rcu()
hci_dev_unlock()
RCU grace period ends
IRK is freed
memcpy(irk->val, val, 16)
bacpy(&irk->rpa, rpa)
The receive worker then writes into freed memory. KASAN reported a
16-byte write in hci_add_irk(), 45 bytes into a freed smp_irk
allocation.
Keep an outer RCU read-side critical section in hci_add_irk() across the
lookup and both copies. Drop it before the GFP_KERNEL allocation and
reacquire it before publishing a new node. Reclamation must then wait
until the stores finish. This also avoids acquiring hdev->lock under the
L2CAP locks, which would invert the existing lock order.
The change has not been runtime-tested.
KASAN report as below:
==================================================================
BUG: KASAN: slab-use-after-free in hci_add_irk+0x77/0x2a0
Write of size 16 at addr ffff88810bbdcc2d by task kworker/u17:0/526
Workqueue: hci0 hci_rx_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
? hci_add_irk+0x77/0x2a0
? srso_alias_return_thunk+0x5/0xfbef5
? __virt_addr_valid+0x20d/0x410
? hci_add_irk+0x77/0x2a0
kasan_report+0xe0/0x110
? hci_add_irk+0x77/0x2a0
kasan_check_range+0x105/0x1b0
__asan_memcpy+0x3c/0x60
hci_add_irk+0x77/0x2a0
smp_recv_cb+0x594d/0x8e40
? __pfx_smp_recv_cb+0x10/0x10
? __pfx___might_resched+0x10/0x10
? srso_alias_return_thunk+0x5/0xfbef5
? stack_depot_save_flags+0x29/0x9a0
? __mutex_lock+0x182c/0x2590
? kasan_save_stack+0x42/0x60
? l2cap_recv_frame+0x9ef/0x8830
? __kasan_slab_free+0x5f/0x80
? kmem_cache_free+0x242/0x580
? srso_alias_return_thunk+0x5/0xfbef5
? find_held_lock+0x2b/0x80
? l2cap_recv_frame+0xaef/0x8830
? __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 520:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_cache_noprof+0x251/0x610
hci_add_irk+0x131/0x2a0
load_irks+0x5fb/0x8c0
hci_sock_sendmsg+0x128b/0x22e0
sock_write_iter+0x4c0/0x550
do_iter_readv_writev+0x59c/0x860
vfs_writev+0x30e/0xc10
do_writev+0x241/0x2f0
do_syscall_64+0x115/0x6a0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
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 ffff88810bbdcc00
which
belongs to the cache kmalloc-64 of size 64
The buggy address is located 45 bytes inside of
freed
64-byte region [ffff88810bbdcc00,
ffff88810bbdcc40)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0
pfn:0x10bbdc
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:
ffff88810bbdcb00: 00 00 00 00 00 00 00 00 fc fc fc fc fc fc fc fc
ffff88810bbdcb80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
>ffff88810bbdcc00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
^
ffff88810bbdcc80: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
ffff88810bbdcd00: fa fb fb fb fb fb fb fb fc fc fc fc fc fc fc fc
==================================================================
Disabling lock debugging due to kernel taint
Fixes: adae20cb2d20 ("Bluetooth: Convert IRK list to 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..f8c274c21c0f7 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -1317,8 +1317,14 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
{
struct smp_irk *irk;
+ /*
+ * Keep the IRK alive until the value and RPA copies are complete.
+ */
+ rcu_read_lock();
irk = hci_find_irk_by_addr(hdev, bdaddr, addr_type);
if (!irk) {
+ rcu_read_unlock();
+
irk = kzalloc_obj(*irk);
if (!irk)
return NULL;
@@ -1326,11 +1332,13 @@ struct smp_irk *hci_add_irk(struct hci_dev *hdev, bdaddr_t *bdaddr,
bacpy(&irk->bdaddr, bdaddr);
irk->addr_type = addr_type;
+ rcu_read_lock();
list_add_rcu(&irk->list, &hdev->identity_resolving_keys);
}
memcpy(irk->val, val, 16);
bacpy(&irk->rpa, rpa);
+ rcu_read_unlock();
return irk;
}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-12 3:02 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-12 3:02 [PATCH] Bluetooth: Keep IRKs alive during updates 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®