Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: MGMT: Reject LTK loads while connections exist
@ 2026-10-13 16:26 Cen Zhang
  2026-10-13 20:08 ` bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Cen Zhang @ 2026-10-13 16:26 UTC (permalink / raw)
  To: marcel, luiz.dentz, johan.hedberg, vinicius.gomes
  Cc: linux-bluetooth, linux-kernel, baijiaju1990, jjzuming, zzzccc427

SMP channels retain LTK pointers from the controller's long_term_keys
list until key notification or pairing teardown. load_long_term_keys()
clears that list under hdev->lock without invalidating the channels'
saved pointers. The SMP channel lock does not serialize the management
clear; kfree_rcu() does not keep a key alive for the rest of pairing.

An ongoing pairing and management LTK loads permit this ordering:

1. smp_cmd_initiator_ident() or sc_add_ltk() saves the hci_add_ltk()
   result A in smp->ltk.
2. load_long_term_keys() takes hdev->lock, and hci_smp_ltks_clear()
   unlinks A and queues its RCU free. The pairing retains its alias.
3. After the RCU grace period, A is freed. A later management load
   allocates LTK B at the same address, possibly on another controller.
4. The old pairing reaches smp_chan_destroy() and reads smp->ltk->type
   through the saved address.

If B is still live, the old pairing can read its type and remove it
through the stale alias; smp_notify_keys() can also overwrite and report
its fields. If B has already been freed, the access is a use-after-free.
The KASAN report below shows the latter ordering: the stale teardown
read follows allocation and RCU release of the replacement LTK.

Reject MGMT_OP_LOAD_LONG_TERM_KEYS with MGMT_STATUS_BUSY while LE or
BR/EDR ACL connections exist. Both transports can have SMP channels
retaining LTKs, including keys derived over BR/EDR. Check the connection
counts under hdev->lock and keep that lock through clearing and loading.
Connections are published under the same lock, and disconnect tears
down SMP before removing the connection from the hash. A successful
load therefore runs without pairing aliases, and a new connection
cannot acquire one until the replacement finishes.

KASAN report as below:

    ==================================================================
    BUG: KASAN: slab-use-after-free in smp_chan_destroy.isra.0+0xc8b/0x1060
    Read of size 1 at addr ffff88810a45eb28 by task kworker/u17:3/506

    Workqueue: hci1 hci_rx_work
    Call Trace:
     <TASK>
     dump_stack_lvl+0x93/0xd0
     print_report+0xce/0x630
     ? smp_chan_destroy.isra.0+0xc8b/0x1060
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __virt_addr_valid+0x20d/0x410
     ? smp_chan_destroy.isra.0+0xc8b/0x1060
     kasan_report+0xe0/0x110
     ? smp_chan_destroy.isra.0+0xc8b/0x1060
     smp_chan_destroy.isra.0+0xc8b/0x1060
     smp_teardown_cb+0xc7/0x230
     l2cap_chan_del+0x123/0x8f0
     l2cap_conn_del+0x31b/0x740
     ? reacquire_held_locks+0x1e7/0x1f0
     ? __pfx_l2cap_disconn_cfm+0x10/0x10
     l2cap_disconn_cfm+0x87/0xd0
     hci_disconn_complete_evt+0x30f/0x990
     ? srso_alias_return_thunk+0x5/0xfbef5
     hci_event_packet+0x894/0xc70
     ? __pfx_hci_disconn_complete_evt+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? __pfx_hci_event_packet+0x10/0x10
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? free_zapped_rcu+0xd0/0x1e0
     ? srso_alias_return_thunk+0x5/0xfbef5
     ? lockdep_hardirqs_on_prepare+0xea/0x1a0
     ? __pfx_hci_cmd_sync_complete+0x10/0x10
     ? trace_hardirqs_on+0x18/0x160
     ? srso_alias_return_thunk+0x5/0xfbef5
     hci_rx_work+0x367/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 523:
     kasan_save_stack+0x33/0x60
     kasan_save_track+0x14/0x30
     __kasan_kmalloc+0xaa/0xb0
     __kmalloc_cache_noprof+0x251/0x610
     hci_add_ltk+0x285/0x4c0
     load_long_term_keys+0x74a/0x8d0
     hci_sock_sendmsg+0x128b/0x22e0
     __sys_sendto+0x425/0x470
     __x64_sys_sendto+0xe5/0x1c0
     do_syscall_64+0x115/0x6a0
     entry_SYSCALL_64_after_hwframe+0x77/0x7f

    Freed by task 523:
     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+0x37/0x80
     asm_sysvec_apic_timer_interrupt+0x1a/0x20

    The buggy address belongs to the object at ffff88810a45eb00
     which belongs to the cache kmalloc-96 of size 96
    The buggy address is located 40 bytes inside of
     freed 96-byte region [ffff88810a45eb00, ffff88810a45eb60)

    The buggy address belongs to the physical page:
        page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0
    pfn:0x10a45e
    flags: 0x200000000000000(node=0|zone=2)
    page_type: f5(slab)
        raw: 0200000000000000 ffff888100042280 dead000000000100
    dead000000000122
        raw: 0000000000000000 0000000000200020 00000000f5000000
    0000000000000000
    page dumped because: kasan: bad access detected

    Memory state around the buggy address:
     ffff88810a45ea00: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
     ffff88810a45ea80: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
    >ffff88810a45eb00: fa fb fb fb fb fb fb fb fb fb fb fb fc fc fc fc
                                      ^
     ffff88810a45eb80: 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc fc
     ffff88810a45ec00: 00 00 00 00 00 00 00 00 00 00 00 fc fc fc fc fc
    ==================================================================

Fixes: 346af67b8d11 ("Bluetooth: Add MGMT handlers for dealing with SMP LTK's")
Assisted-by: LLM
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9f65863e7457d..c3e8ca48a0337 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -7460,6 +7460,16 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 
 	hci_dev_lock(hdev);
 
+	/*
+	 * SMP over LE or BR/EDR may retain pointers to the current LTKs.
+	 */
+	if (hci_conn_num(hdev, LE_LINK) || hci_conn_num(hdev, ACL_LINK)) {
+		err = mgmt_cmd_status(sk, hdev->id,
+				      MGMT_OP_LOAD_LONG_TERM_KEYS,
+				      MGMT_STATUS_BUSY);
+		goto unlock;
+	}
+
 	hci_smp_ltks_clear(hdev);
 
 	for (i = 0; i < key_count; i++) {
@@ -7513,6 +7523,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
 	err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0,
 			   NULL, 0);
 
+unlock:
 	hci_dev_unlock(hdev);
 
 	return err;

^ permalink raw reply related	[flat|nested] 2+ messages in thread

* RE: Bluetooth: MGMT: Reject LTK loads while connections exist
  2026-10-13 16:26 [PATCH] Bluetooth: MGMT: Reject LTK loads while connections exist Cen Zhang
@ 2026-10-13 20:08 ` bluez.test.bot
  0 siblings, 0 replies; 2+ messages in thread
From: bluez.test.bot @ 2026-10-13 20:08 UTC (permalink / raw)
  To: linux-bluetooth, zzzccc427

[-- Attachment #1: Type: text/plain, Size: 1126 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/series/1184929/

---Test result---

Test Summary:
CheckPatch                    PASS      0.48 seconds
VerifyFixes                   PASS      0.45 seconds
VerifySignedoff               PASS      0.07 seconds
GitLint                       PASS      0.31 seconds
SubjectPrefix                 PASS      0.43 seconds
BuildKernel                   PASS      18.09 seconds
CheckAllWarning               PASS      21.23 seconds
CheckSparse                   PASS      22.86 seconds
BuildKernel32                 PASS      18.59 seconds
CheckKernelLLVM               PASS      19.78 seconds
TestRunnerSetup               PASS      414.80 seconds
TestRunner_mgmt-tester        PASS      80.06 seconds
TestRunner_mesh-tester        PASS      4.96 seconds
IncrementalBuild              PASS      18.03 seconds



https://github.com/bluez/bluetooth-next/pull/920

---
Regards,
Linux Bluetooth


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-13 20:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 16:26 [PATCH] Bluetooth: MGMT: Reject LTK loads while connections exist Cen Zhang
2026-10-13 20:08 ` bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox