From add463a65802bb117e4a17b8c00d747fa70399fc Mon Sep 17 00:00:00 2001 From: Cen Zhang Date: Thu, 29 Oct 2026 21:12:28 +0800 Subject: [PATCH] Bluetooth: SMP: Protect LTK encryption with RCU smp_ltk_encrypt() checks the security level of the LTK returned by hci_find_ltk() and then starts encryption with its EDIV, Rand, value and key size. The finder ends its RCU read-side section before returning the borrowed entry, and neither caller holds hdev->lock: the SMP Security Request handler runs from the receive path, and setsockopt(BT_SECURITY) reaches smp_conn_security() with only the socket and channel locks held. MGMT_OP_LOAD_LONG_TERM_KEYS can therefore free the LTK in between: Socket security request Management / RCU reclamation smp_ltk_encrypt() key = hci_find_ltk() load_long_term_keys() hci_dev_lock(hdev) hci_smp_ltks_clear(hdev) list_del_rcu(), kfree_rcu() hci_dev_unlock(hdev) grace period ends, key is freed smp_ltk_sec_level(key) hci_le_start_enc(hcon, key->ediv, key->rand, key->val, ...) Keep an RCU read-side section across the lookup and every use of the key. hci_le_start_enc() copies the key into the command and queues it with a GFP_ATOMIC allocation, so the whole sequence can stay inside the section. KFENCE reported this on a test kernel where the socket thread was paused after the lookup until the RCU callback had freed the key. KFENCE report as below: ================================================================== BUG: KFENCE: use-after-free read in smp_ltk_encrypt.isra.0+0xf3/0x370 Use-after-free read at 0x00000000c45332eb (in kfence-#128): smp_ltk_encrypt.isra.0+0xf3/0x370 smp_conn_security+0x489/0x9d0 l2cap_sock_setsockopt+0x1bbe/0x2300 do_sock_setsockopt+0x1fb/0x430 __sys_setsockopt+0x106/0x180 __x64_sys_setsockopt+0xc2/0x160 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f kfence-#128: 0x00000000d45af9b0-0x0000000053c1365e, size=72, cache=kmalloc-96 allocated by task 533 on cpu 0 at 50.484258s (0.449177s ago): hci_add_ltk+0x1f5/0x3a0 load_long_term_keys+0x6ea/0x930 hci_sock_sendmsg+0x128b/0x22e0 sock_write_iter+0x4c0/0x550 vfs_write+0x637/0x1010 ksys_write+0x1b7/0x200 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f freed by task 0 on cpu 0 at 50.846944s (0.093664s ago): __rcu_free_sheaf_prepare+0x261/0x2a0 rcu_free_sheaf_nobarn+0x1d/0x50 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 pv_native_safe_halt+0xf/0x20 default_idle+0x9/0x10 default_idle_call+0x6c/0xb0 do_idle+0x3a7/0x650 cpu_startup_entry+0x54/0x60 rest_init+0x15e/0x2b0 start_kernel+0x412/0x4b0 x86_64_start_reservations+0x18/0x30 x86_64_start_kernel+0x111/0x120 common_startup_64+0x13e/0x158 CPU: 2 UID: 0 PID: 534 Comm: l2cap-ltk-actor Not tainted 7.2.0-rc6-pmb-bt-functional-v1+ #5 PREEMPT(lazy) Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014 ================================================================== Fixes: 988c5997d320 ("Bluetooth: Use the LTK after receiving a LE Security Request") Assisted-by: LLM Signed-off-by: Cen Zhang Signed-off-by: Luiz Augusto von Dentz --- net/bluetooth/smp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1d3f3ceb3ecc..b7015e37e1f1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -2209,16 +2209,20 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) { struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; + bool ret = false; + + rcu_read_lock(); key = hci_find_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->role); if (!key) - return false; + goto unlock; if (smp_ltk_sec_level(key) < sec_level) - return false; + goto unlock; + ret = true; if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, hcon->flags)) - return true; + goto unlock; hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size); hcon->enc_key_size = key->enc_size; @@ -2226,7 +2230,9 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) /* We never store STKs for initiator role, so clear this flag */ clear_bit(HCI_CONN_STK_ENCRYPT, hcon->flags); - return true; +unlock: + rcu_read_unlock(); + return ret; } bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level,