* [PATCH] Bluetooth: mgmt: Fix UAF in mesh_send_sync caused by race in mesh queue
@ 2026-10-14 16:05 Igor Putko
0 siblings, 0 replies; only message in thread
From: Igor Putko @ 2026-10-14 16:05 UTC (permalink / raw)
To: Luiz Augusto von Dentz, Marcel Holtmann
Cc: linux-bluetooth, linux-kernel, syzbot+f20755f37da70df33a16,
Igor Putko
A race condition between mesh_send() and mesh_next() can lead to the
same struct mgmt_mesh_tx object being queued twice into the cmd_sync
work list.
Specifically, mesh_send_done_sync() and mesh_send_start_complete() clear
the HCI_MESH_SENDING flag before mesh_next() is executed or without
holding hci_dev_lock. If a concurrent mesh_send() arrives during this
window, it observes HCI_MESH_SENDING as cleared, adds the new mesh_tx
to hdev->mesh_pending, and queues it via hci_cmd_sync_queue().
Immediately thereafter, mesh_next() executes without holding
hci_dev_lock, inspects hdev->mesh_pending with mgmt_mesh_next(), and
queues the very same mesh_tx a second time.
When the first queued entry finishes, its completion callback invokes
mesh_send_complete(), which frees the mesh_tx structure. When the worker
subsequently picks up the duplicate second entry, mesh_send_sync()
attempts to access fields in the already freed mesh_tx, triggering a
KASAN slab-use-after-free read.
Fix this by:
1. Synchronizing mesh_next() under hci_dev_lock and checking if
HCI_MESH_SENDING is already set before queueing.
2. Holding hci_dev_lock when modifying HCI_MESH_SENDING and completing
mesh transactions in mesh_send_done_sync() and
mesh_send_start_complete().
3. Unconditionally removing mesh_tx if hci_cmd_sync_queue() fails in
mesh_send().
Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh")
Reported-by: syzbot+f20755f37da70df33a16@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?id=d2762f31ce58dfcb900c2db500bf361f165c07bc
Signed-off-by: Igor Putko <igorpetindev@gmail.com>
---
net/bluetooth/mgmt.c | 37 ++++++++++++++++++++++++++-----------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 251a896babd5..0dda38af00ec 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1094,13 +1094,15 @@ static int mesh_send_done_sync(struct hci_dev *hdev, void *data)
{
struct mgmt_mesh_tx *mesh_tx;
- hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
if (list_empty(&hdev->adv_instances))
hci_disable_advertising_sync(hdev);
- mesh_tx = mgmt_mesh_next(hdev, NULL);
+ hci_dev_lock(hdev);
+ mesh_tx = mgmt_mesh_next(hdev, NULL);
if (mesh_tx)
mesh_send_complete(hdev, mesh_tx, false);
+ hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
+ hci_dev_unlock(hdev);
return 0;
}
@@ -1109,18 +1111,30 @@ static int mesh_send_sync(struct hci_dev *hdev, void *data);
static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err);
static void mesh_next(struct hci_dev *hdev, void *data, int err)
{
- struct mgmt_mesh_tx *mesh_tx = mgmt_mesh_next(hdev, NULL);
+ struct mgmt_mesh_tx *mesh_tx;
+ int ret;
- if (!mesh_tx)
+ hci_dev_lock(hdev);
+
+ if (hci_dev_test_flag(hdev, HCI_MESH_SENDING)) {
+ hci_dev_unlock(hdev);
return;
+ }
- err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
- mesh_send_start_complete);
+ mesh_tx = mgmt_mesh_next(hdev, NULL);
+ if (!mesh_tx) {
+ hci_dev_unlock(hdev);
+ return;
+ }
- if (err < 0)
+ ret = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
+ mesh_send_start_complete);
+ if (ret < 0)
mesh_send_complete(hdev, mesh_tx, false);
else
hci_dev_set_flag(hdev, HCI_MESH_SENDING);
+
+ hci_dev_unlock(hdev);
}
static void mesh_send_done(struct work_struct *work)
@@ -2309,9 +2323,12 @@ static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err)
/* Report any errors here, but don't report completion */
if (mgmt_err) {
+ hci_dev_lock(hdev);
hci_dev_clear_flag(hdev, HCI_MESH_SENDING);
/* Send Complete Error Code for handle */
mesh_send_complete(hdev, mesh_tx, false);
+ hci_dev_unlock(hdev);
+
if (err != -ECANCELED)
mesh_next(hdev, NULL, 0);
return;
@@ -2541,10 +2558,8 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_MESH_SEND,
MGMT_STATUS_FAILED);
- if (mesh_tx) {
- if (sending)
- mgmt_mesh_remove(mesh_tx);
- }
+ if (mesh_tx)
+ mgmt_mesh_remove(mesh_tx);
} else {
hci_dev_set_flag(hdev, HCI_MESH_SENDING);
--
2.47.3
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-14 13:05 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 16:05 [PATCH] Bluetooth: mgmt: Fix UAF in mesh_send_sync caused by race in mesh queue Igor Putko
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.