From: Baul Lee <baul.lee@xbow.com>
To: Luiz Augusto von Dentz <luiz.dentz@gmail.com>,
Marcel Holtmann <marcel@holtmann.org>,
Brian Gix <brian.gix@intel.com>
Cc: Baul Lee <baul.lee@xbow.com>,
Luiz Augusto von Dentz <luiz.von.dentz@intel.com>,
Dmitry Antipov <dmantipov@yandex.ru>,
linux-bluetooth@vger.kernel.org, linux-kernel@vger.kernel.org,
federico.kirschbaum@xbow.com, stable@vger.kernel.org
Subject: [PATCH v2 3/3] Bluetooth: MGMT: reference-count struct mgmt_mesh_tx
Date: Fri, 7 Aug 2026 19:15:29 +0900 [thread overview]
Message-ID: <20260807101529.17348-4-baul.lee@xbow.com> (raw)
In-Reply-To: <20260807101529.17348-1-baul.lee@xbow.com>
hci_cmd_sync_submit() stores the caller's pointer in a work entry and
takes no reference to what it names. hci_cmd_sync_work() drains that
list from the head on an ordered workqueue, so an entry queued earlier
runs to completion before a later one is looked at.
mesh_send() links a struct mgmt_mesh_tx onto hdev->mesh_pending and
queues mesh_send_sync() with the raw pointer. A MESH_SEND_CANCEL issued
before that send leaves its send_cancel() entry ahead in the queue, so
send_cancel() runs first: it picks the object out of hdev->mesh_pending
with mgmt_mesh_next(), which returns it without unlinking it, and frees
it through mesh_send_complete(). Nothing invalidates the pointer the
later entry still holds, so mesh_send_sync() and its destroy callback
mesh_send_start_complete() run on freed memory:
[ 43.618774] BUG: KASAN: slab-use-after-free in mesh_send_sync+0xec/0x1b4
[ 43.618851] Write of size 1 at addr ffff000009f2a6a9 by task kworker/u5:2/148
[ 43.619464] mesh_send_sync+0xec/0x1b4
[ 43.619531] hci_cmd_sync_work+0xac/0x128
[ 43.620882] Freed by task 148:
[ 43.621171] mgmt_mesh_remove+0x94/0x110
[ 43.621218] send_cancel+0xd8/0x1cc
[ 43.621270] hci_cmd_sync_work+0xac/0x128
Offset 41 is mesh_tx->instance, the only byte mesh_send_sync() stores
through mesh_tx; it reads further fields of the same freed object, and
hci_set_adv_instance_data() copies 31 of those bytes into a live
struct adv_info. The free and the use are consecutive iterations of one
hci_cmd_sync_work() loop on one kworker, so queue order alone decides
it.
Give struct mgmt_mesh_tx a reference count. hdev->mesh_pending holds
one, and every pointer handed to hci_cmd_sync_queue() takes a second one
that the destroy callback drops, so a cancel that unlinks the object
while a work entry is still queued no longer releases it.
mgmt_mesh_remove() becomes an unlink plus a put and returns early when
the object is already unlinked, because it can now outlive its removal
from the list. The list reference is still dropped under hci_dev_lock()
only, so mesh_send()'s use of mesh_tx->handle after queueing stays
covered by the lock it holds.
A cancel that arrives before the queued mesh_send_sync() now lets that
send run rather than freeing the object under it; suppressing the
transmission as well is a separate change.
hci_cmd_sync_dequeue() is the other in-tree option, but
mgmt_mesh_remove() is not given the hci_dev it needs and cannot tell
whether the work entry has already been taken off cmd_sync_work_list.
Discovered by XBOW, triaged by Baul Lee <baul.lee@xbow.com>
Fixes: b338d91703fa ("Bluetooth: Implement support for Mesh")
Cc: stable@vger.kernel.org
Signed-off-by: Baul Lee <baul.lee@xbow.com>
---
net/bluetooth/mgmt.c | 25 +++++++++++++++++--------
net/bluetooth/mgmt_util.c | 23 +++++++++++++++++++++--
net/bluetooth/mgmt_util.h | 3 +++
3 files changed, 41 insertions(+), 10 deletions(-)
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 853a80fd15af..61d279ae2f71 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1133,13 +1133,15 @@ static void mesh_next(struct hci_dev *hdev, void *data, int err)
return;
}
- err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
+ err = hci_cmd_sync_queue(hdev, mesh_send_sync, mgmt_mesh_get(mesh_tx),
mesh_send_start_complete);
- if (err < 0)
+ if (err < 0) {
+ mgmt_mesh_put(mesh_tx);
mesh_send_complete(hdev, mesh_tx, false);
- else
+ } else {
hci_dev_set_flag(hdev, HCI_MESH_SENDING);
+ }
hci_dev_unlock(hdev);
}
@@ -2328,7 +2330,7 @@ static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err)
u8 mgmt_err = mgmt_status(err);
if (err == -ECANCELED)
- return;
+ goto put;
/* Report any errors here, but don't report completion */
@@ -2338,12 +2340,15 @@ static void mesh_send_start_complete(struct hci_dev *hdev, void *data, int err)
hci_dev_lock(hdev);
mesh_send_complete(hdev, mesh_tx, false);
hci_dev_unlock(hdev);
- return;
+ goto put;
}
mesh_send_interval = msecs_to_jiffies((send->cnt) * 25);
queue_delayed_work(hdev->req_workqueue, &hdev->mesh_send_done,
mesh_send_interval);
+
+put:
+ mgmt_mesh_put(mesh_tx);
}
static int mesh_send_sync(struct hci_dev *hdev, void *data)
@@ -2549,11 +2554,15 @@ static int mesh_send(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
sending = hci_dev_test_flag(hdev, HCI_MESH_SENDING);
mesh_tx = mgmt_mesh_add(sk, hdev, send, len);
- if (!mesh_tx)
+ if (!mesh_tx) {
err = -ENOMEM;
- else if (!sending)
- err = hci_cmd_sync_queue(hdev, mesh_send_sync, mesh_tx,
+ } else if (!sending) {
+ err = hci_cmd_sync_queue(hdev, mesh_send_sync,
+ mgmt_mesh_get(mesh_tx),
mesh_send_start_complete);
+ if (err < 0)
+ mgmt_mesh_put(mesh_tx);
+ }
if (err < 0) {
bt_dev_err(hdev, "Send Mesh Failed %d", err);
diff --git a/net/bluetooth/mgmt_util.c b/net/bluetooth/mgmt_util.c
index a822091f2907..c7543964525a 100644
--- a/net/bluetooth/mgmt_util.c
+++ b/net/bluetooth/mgmt_util.c
@@ -422,6 +422,7 @@ struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
if (!mesh_tx)
return NULL;
+ refcount_set(&mesh_tx->ref, 1);
hdev->mesh_send_ref++;
if (!hdev->mesh_send_ref)
hdev->mesh_send_ref++;
@@ -438,9 +439,27 @@ struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
return mesh_tx;
}
-void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx)
+struct mgmt_mesh_tx *mgmt_mesh_get(struct mgmt_mesh_tx *mesh_tx)
+{
+ refcount_inc(&mesh_tx->ref);
+
+ return mesh_tx;
+}
+
+void mgmt_mesh_put(struct mgmt_mesh_tx *mesh_tx)
{
- list_del(&mesh_tx->list);
+ if (!refcount_dec_and_test(&mesh_tx->ref))
+ return;
+
sock_put(mesh_tx->sk);
kfree(mesh_tx);
}
+
+void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx)
+{
+ if (list_empty(&mesh_tx->list))
+ return;
+
+ list_del_init(&mesh_tx->list);
+ mgmt_mesh_put(mesh_tx);
+}
diff --git a/net/bluetooth/mgmt_util.h b/net/bluetooth/mgmt_util.h
index 20810cf06e81..b38970c1332c 100644
--- a/net/bluetooth/mgmt_util.h
+++ b/net/bluetooth/mgmt_util.h
@@ -19,6 +19,7 @@
struct mgmt_mesh_tx {
struct list_head list;
+ refcount_t ref;
int index;
size_t param_len;
struct sock *sk;
@@ -72,4 +73,6 @@ struct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle);
struct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk);
struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len);
+struct mgmt_mesh_tx *mgmt_mesh_get(struct mgmt_mesh_tx *mesh_tx);
+void mgmt_mesh_put(struct mgmt_mesh_tx *mesh_tx);
void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx);
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-08-07 10:15 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 10:15 [PATCH v2 0/3] Bluetooth: MGMT: fix use-after-free of struct mgmt_mesh_tx Baul Lee
2026-08-07 10:15 ` [PATCH v2 1/3] Bluetooth: MGMT: remove the mesh walk from the socket destructor Baul Lee
2026-08-07 11:25 ` Bluetooth: MGMT: fix use-after-free of struct mgmt_mesh_tx bluez.test.bot
2026-08-07 10:15 ` [PATCH v2 2/3] Bluetooth: MGMT: protect hdev->mesh_pending with hdev->lock Baul Lee
2026-08-07 10:15 ` Baul Lee [this message]
2026-08-07 16:12 ` [PATCH v2 0/3] Bluetooth: MGMT: fix use-after-free of struct mgmt_mesh_tx Luiz Augusto von Dentz
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=20260807101529.17348-4-baul.lee@xbow.com \
--to=baul.lee@xbow.com \
--cc=brian.gix@intel.com \
--cc=dmantipov@yandex.ru \
--cc=federico.kirschbaum@xbow.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=luiz.dentz@gmail.com \
--cc=luiz.von.dentz@intel.com \
--cc=marcel@holtmann.org \
--cc=stable@vger.kernel.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