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: Keep IRKs alive during updates
Date: Mon, 12 Oct 2026 11:02:30 +0800 [thread overview]
Message-ID: <pm-bluetooth-objects-candidate-0191-v7-12caee3a7046a1a8f311@gmail.com> (raw)
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;
}
next reply other threads:[~2026-10-12 3:02 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 3:02 Cen Zhang [this message]
2026-10-12 8:07 ` Bluetooth: Keep IRKs alive during updates bluez.test.bot
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-0191-v7-12caee3a7046a1a8f311@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.