From: Cen Zhang <zzzccc427@gmail.com>
To: marcel@holtmann.org, luiz.dentz@gmail.com
Cc: linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, jjzuming@gmail.com, zzzccc427@gmail.com
Subject: [PATCH v2 2/2] Bluetooth: SMP: Serialize root channel replacement
Date: Sat, 31 Oct 2026 11:27:14 +0800 [thread overview]
Message-ID: <pm-bluetooth-objects-candidate-0184-v8-60106ad0c49170f5bfb4-2@gmail.com> (raw)
In-Reply-To: <pm-bluetooth-objects-candidate-0184-v8-60106ad0c49170f5bfb4-0@gmail.com>
smp_register() replaces the SMP root channels when they are still
registered from an earlier power-on. This happens when management
powers on a controller that is already up with HCI_AUTO_OFF set:
hci_power_on_sync() clears the flag and runs hci_powered_update_sync()
again, which registers SMP so that the channels use the identity address
chosen for this power-on. The WARN_ON() in smp_register() fires on this
path, and the old channels and their SMP data are freed while two kinds
of users can still hold them.
HCI event handlers read hdev->smp_data under hdev->lock, for example
smp_irk_matches() when resolving the target of a directed advertising
report:
HCI receive worker Power-on request
hci_dev_lock(hdev)
smp_irk_matches()
chan = hdev->smp_data
hci_powered_update_sync()
smp_register()
smp_del_chan(chan), chan freed
reads chan->data
SMP receive callbacks run on hdev->workqueue without hdev->lock and use
the ECDH transform of the root, for example smp_cmd_public_key():
HCI receive worker (SMP) Power-on request
smp_cmd_public_key()
tfm = root's tfm_ecdh
smp_register()
smp_del_chan()
crypto_free_kpp(tfm)
compute_ecdh_secret(tfm, ...)
Before commit 2ff13894cfb8 ("Bluetooth: Perform HCI update for power on
synchronously") moved it to the request worker, registration ran from the
completion callback of the power-on request in the HCI receive worker,
which serialized it with both users.
Run the registration on hdev->workqueue again and hold hdev->lock while
it replaces the channels. smp_register() waits for the work and returns
its result. Its callers hold the request lock but not hdev->lock, and no
work on hdev->workqueue takes the request lock; hci_dev_close_sync()
already drains this workqueue with the request lock held. Users that run
under the request lock, such as smp_generate_rpa(), stay serialized by
that lock.
The reports below come from test kernels with scheduling hooks that
widen these windows. The WARN_ON() in smp_register() was printed before
each of them.
KASAN report as below:
==================================================================
BUG: KASAN: slab-use-after-free in smp_irk_matches+0x2fb/0x310
Read of size 8 at addr ffff88810c35f4a0 by task kworker/u17:0/520
Workqueue: hci0 hci_rx_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
kasan_report+0xe0/0x110
smp_irk_matches+0x2fb/0x310
process_adv_report.part.0+0x2ce/0x1680
hci_le_direct_adv_report_evt+0x2f8/0x4c0
hci_le_meta_evt+0x30c/0x5c0
hci_event_packet+0x4e8/0xc70
hci_rx_work+0x367/0xf00
process_one_work+0x908/0x19c0
worker_thread+0x65c/0xe40
kthread+0x34f/0x460
ret_from_fork+0x659/0x940
ret_from_fork_asm+0x1a/0x30
</TASK>
Allocated by task 522:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_cache_noprof+0x251/0x610
l2cap_chan_create+0x47/0x930
smp_add_cid+0x120/0x4f0
smp_register+0xa7/0x400
hci_powered_update_sync+0x88/0x720
hci_dev_open_sync+0xae0/0x2060
hci_set_powered_sync+0x2a9/0x720
set_powered_sync+0xaa/0x150
hci_cmd_sync_work+0x1b3/0x450
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 522:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x5f/0x80
kfree+0x307/0x580
l2cap_chan_put+0x273/0x3a0
smp_del_chan+0x9f/0x170
smp_register+0x2e8/0x400
hci_powered_update_sync+0x88/0x720
hci_set_powered_sync+0x5e5/0x720
set_powered_sync+0xaa/0x150
hci_cmd_sync_work+0x1b3/0x450
process_one_work+0x908/0x19c0
worker_thread+0x65c/0xe40
kthread+0x34f/0x460
ret_from_fork+0x659/0x940
ret_from_fork_asm+0x1a/0x30
The buggy address belongs to the object at ffff88810c35f000
which belongs to the cache kmalloc-2k of size 2048
The buggy address is located 1184 bytes inside of
freed 2048-byte region [ffff88810c35f000, ffff88810c35f800)
==================================================================
KASAN report as below:
==================================================================
BUG: KASAN: slab-use-after-free in compute_ecdh_secret+0x856/0xa50
Read of size 4 at addr ffff8881071f4f00 by task kworker/u17:2/500
Workqueue: hci1 hci_rx_work
Call Trace:
<TASK>
dump_stack_lvl+0x93/0xd0
print_report+0xce/0x630
kasan_report+0xe0/0x110
compute_ecdh_secret+0x856/0xa50
smp_recv_cb+0x3acf/0x93b0
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
</TASK>
Allocated by task 497:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
__kasan_kmalloc+0xaa/0xb0
__kmalloc_node_noprof+0x2f2/0x7b0
crypto_create_tfm_node+0x92/0x390
crypto_alloc_tfm_node+0xf5/0x220
smp_add_cid+0xc1/0x4f0
smp_register+0x9d/0x430
hci_powered_update_sync+0x82/0x680
hci_set_powered_sync+0x4f4/0x750
set_powered_sync+0xb8/0x210
hci_cmd_sync_work+0x1b3/0x450
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 497:
kasan_save_stack+0x33/0x60
kasan_save_track+0x14/0x30
kasan_save_free_info+0x3b/0x60
__kasan_slab_free+0x5f/0x80
kfree+0x307/0x580
crypto_destroy_tfm+0xdd/0x230
smp_del_chan+0x8b/0x180
smp_register+0x29d/0x430
hci_powered_update_sync+0x82/0x680
hci_set_powered_sync+0x4f4/0x750
set_powered_sync+0xb8/0x210
hci_cmd_sync_work+0x1b3/0x450
process_one_work+0x908/0x19c0
worker_thread+0x65c/0xe40
kthread+0x34f/0x460
ret_from_fork+0x659/0x940
ret_from_fork_asm+0x1a/0x30
The buggy address belongs to the object at ffff8881071f4f00
which belongs to the cache kmalloc-128 of size 128
The buggy address is located 0 bytes inside of
freed 128-byte region [ffff8881071f4f00, ffff8881071f4f80)
==================================================================
Fixes: 2ff13894cfb8 ("Bluetooth: Perform HCI update for power on synchronously")
Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
---
Assisted-by: LLM
net/bluetooth/smp.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 0e442cc2a35da..552b6b3713c3a 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -3369,7 +3369,8 @@ int smp_force_bredr(struct hci_dev *hdev, bool enable)
return 0;
}
-int smp_register(struct hci_dev *hdev)
+static int __smp_register(struct hci_dev *hdev)
+ __must_hold(&hdev->lock)
{
struct l2cap_chan *chan;
@@ -3419,6 +3420,39 @@ int smp_register(struct hci_dev *hdev)
return 0;
}
+struct smp_register_work {
+ struct work_struct work;
+ struct hci_dev *hdev;
+ int err;
+};
+
+static void smp_register_func(struct work_struct *work)
+{
+ struct smp_register_work *reg =
+ container_of(work, struct smp_register_work, work);
+ struct hci_dev *hdev = reg->hdev;
+
+ hci_dev_lock(hdev);
+ reg->err = __smp_register(hdev);
+ hci_dev_unlock(hdev);
+}
+
+int smp_register(struct hci_dev *hdev)
+{
+ struct smp_register_work reg = { .hdev = hdev };
+
+ /* HCI event handlers use the root channels under hdev->lock, and
+ * SMP receive callbacks use them from hdev->workqueue. Replace the
+ * channels from that workqueue while holding hdev->lock.
+ */
+ INIT_WORK_ONSTACK(®.work, smp_register_func);
+ queue_work(hdev->workqueue, ®.work);
+ flush_work(®.work);
+ destroy_work_on_stack(®.work);
+
+ return reg.err;
+}
+
void smp_unregister(struct hci_dev *hdev)
{
struct l2cap_chan *chan;
prev parent reply other threads:[~2026-10-31 3:27 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-31 3:27 [PATCH v2 0/2] Bluetooth: SMP: Serialize root channel updates Cen Zhang
2026-10-31 3:27 ` [PATCH v2 1/2] Bluetooth: SMP: Serialize forced BR/EDR channel changes Cen Zhang
2026-10-31 3:27 ` Cen Zhang [this message]
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-0184-v8-60106ad0c49170f5bfb4-2@gmail.com \
--to=zzzccc427@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=jjzuming@gmail.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®