public inbox for linux-bluetooth@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] Bluetooth: hci_conn: Fix UAF in create_big_sync and create_big_complete
@ 2026-03-04 20:59 Kai Zen
  2026-03-04 21:32 ` bluez.test.bot
  0 siblings, 1 reply; 2+ messages in thread
From: Kai Zen @ 2026-03-04 20:59 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: luiz.von.dentz, stable, marcel


[-- Attachment #1.1: Type: text/plain, Size: 3728 bytes --]

Hello maintainers,

While auditing the hci_cmd_sync_queue() callback sweep that introduced
hci_conn_valid() guards (following the hci_enhanced_setup_sync UAF in
commit 98ccd44002d8), I found that create_big_sync() and
create_big_complete() in net/bluetooth/hci_conn.c were missed.

Both functions receive a raw hci_conn pointer as 'data' but dereference
it without the validity check present in every sibling callback. The
race and fix are described in the commit message below.

The patch is against bluetooth-next/master (2026-03-04).

Thanks,
Kai

---

create_big_sync() and create_big_complete() are queued via
hci_cmd_sync_queue() with a raw hci_conn pointer as 'data', but unlike
all other hci_cmd_sync_queue() callbacks that receive an hci_conn pointer
they lack an hci_conn_valid() guard.

If the connection is torn down after the work is queued but before (or
during) execution, the work dereferences a freed hci_conn object.

Race path:
  1. hci_connect_bis() queues create_big_sync(conn) on hdev->req_workqueue
  2. ISO socket close() triggers hci_conn_drop(); for BIS_LINK timeo=0,
     so disc_work fires immediately on hdev->workqueue
  3. disc_work -> hci_abort_conn -> hci_conn_del() frees conn
  4. create_big_sync() dequeued and runs on req_workqueue; conn is
     already freed -> slab-use-after-free

The two workqueues are distinct (req_workqueue vs workqueue). The only
lock held by create_big_sync is hci_req_sync_lock (hdev->req_lock); the
deletion path in HCI event handlers holds only hci_dev_lock (hdev->lock).
There is no shared lock preventing concurrent execution.

This is the same bug class that was fixed for hci_enhanced_setup_sync
in commit 98ccd44002d8 ("Bluetooth: hci_conn: Fix UAF in
hci_enhanced_setup_sync"), and for hci_le_create_conn_sync,
hci_le_pa_create_sync, hci_le_big_create_sync, hci_acl_create_conn_sync
in subsequent sweeps. create_big_sync and create_big_complete in
hci_conn.c were not included in those sweeps.

Expected KASAN report (analogous to hci_enhanced_setup_sync):

  BUG: KASAN: slab-use-after-free in create_big_sync+0x.../...
  Read of size N at addr ... by task kworker/.../...
  Workqueue: hci0 hci_cmd_sync_work
  ...
  Freed by:
    hci_conn_del+...
    hci_abort_conn_sync+...
    hci_cmd_sync_work+...

Fix: add hci_conn_valid() guard at the start of both functions, matching
the pattern established for all sibling callbacks. Note that
create_big_sync must not dereference conn->iso_qos before the validity
check, so the 'qos' pointer assignment is moved past the check.

Fixes: eca0ae4aea66 ("Bluetooth: Add initial implementation of BIS
connections")
Cc: stable@vger.kernel.org
Signed-off-by: Kai Aizen <kai.aizen.dev@gmail.com>
---
 net/bluetooth/hci_conn.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 4719dac07190..2821bf786a6b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2119,9 +2119,14 @@ static int create_big_sync(struct hci_dev *hdev,
void *data)
 {
        struct hci_conn *conn = data;
-       struct bt_iso_qos *qos = &conn->iso_qos;
        u16 interval, sync_interval = 0;
        u32 flags = 0;
        int err;
+       struct bt_iso_qos *qos;
+
+       if (!hci_conn_valid(hdev, conn))
+               return -ECANCELED;
+
+       qos = &conn->iso_qos;

        if (qos->bcast.out.phys == BIT(1))
                flags |= MGMT_ADV_FLAG_SEC_2M;
@@ -2198,6 +2203,9 @@ static void create_big_complete(struct hci_dev *hdev,
void *data, int err)
 {
        struct hci_conn *conn = data;

+       if (!hci_conn_valid(hdev, conn))
+               return;
+
        bt_dev_dbg(hdev, "conn %p", conn);

        if (err) {

[-- Attachment #1.2: Type: text/html, Size: 4598 bytes --]

[-- Attachment #2: 0001-Bluetooth-hci_conn-Fix-UAF-in-create_big_sync-and-cr.patch --]
[-- Type: text/x-diff, Size: 3298 bytes --]

From SnailSploit <kai@snailsploit.com> Mon Sep 17 00:00:00 2001
From: Kai Aizen <kai@snailsploit.com>
Date: Wed, 4 Mar 2026 20:00:00 +0200
Subject: [PATCH] Bluetooth: hci_conn: Fix UAF in create_big_sync and
 create_big_complete

create_big_sync() and create_big_complete() are queued via
hci_cmd_sync_queue() with a raw hci_conn pointer as 'data', but unlike
all other hci_cmd_sync_queue() callbacks that receive an hci_conn pointer
they lack an hci_conn_valid() guard.

If the connection is torn down after the work is queued but before (or
during) execution, the work dereferences a freed hci_conn object.

Race path:
  1. hci_connect_bis() queues create_big_sync(conn) on hdev->req_workqueue
  2. ISO socket close() triggers hci_conn_drop(); for BIS_LINK timeo=0,
     so disc_work fires immediately on hdev->workqueue
  3. disc_work → hci_abort_conn → hci_conn_del() frees conn
  4. create_big_sync() dequeued and runs on req_workqueue; conn is
     already freed → slab-use-after-free

The two workqueues are distinct (req_workqueue vs workqueue). The only
lock held by create_big_sync is hci_req_sync_lock (hdev->req_lock); the
deletion path in HCI event handlers holds only hci_dev_lock (hdev->lock).
There is no shared lock preventing concurrent execution.

This is the same bug class that was fixed for hci_enhanced_setup_sync
in commit 98ccd44002d8 ("Bluetooth: hci_conn: Fix UAF in
hci_enhanced_setup_sync"), and for hci_le_create_conn_sync,
hci_le_pa_create_sync, hci_le_big_create_sync, hci_acl_create_conn_sync
in subsequent sweeps. create_big_sync and create_big_complete in
hci_conn.c were not included in those sweeps.

Expected KASAN report (analogous to hci_enhanced_setup_sync):

  BUG: KASAN: slab-use-after-free in create_big_sync+0x.../...
  Read of size N at addr ... by task kworker/.../...
  Workqueue: hci0 hci_cmd_sync_work
  ...
  Freed by:
    hci_conn_del+...
    hci_abort_conn_sync+...
    hci_cmd_sync_work+...

Fix: add hci_conn_valid() guard at the start of both functions, matching
the pattern established for all sibling callbacks. Note that
create_big_sync must not dereference conn->iso_qos before the validity
check, so the 'qos' pointer assignment is moved past the check.

Fixes: eca0ae4aea66 ("Bluetooth: Add initial implementation of BIS connections")
Cc: stable@vger.kernel.org
Signed-off-by: Kai Aizen <kai@snailsploit.com>
---
 net/bluetooth/hci_conn.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 4719dac07190..2821bf786a6b 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -2119,9 +2119,14 @@ static int create_big_sync(struct hci_dev *hdev, void *data)
 {
 	struct hci_conn *conn = data;
-	struct bt_iso_qos *qos = &conn->iso_qos;
 	u16 interval, sync_interval = 0;
 	u32 flags = 0;
 	int err;
+	struct bt_iso_qos *qos;
+
+	if (!hci_conn_valid(hdev, conn))
+		return -ECANCELED;
+
+	qos = &conn->iso_qos;
 
 	if (qos->bcast.out.phys == BIT(1))
 		flags |= MGMT_ADV_FLAG_SEC_2M;
@@ -2198,6 +2203,9 @@ static void create_big_complete(struct hci_dev *hdev, void *data, int err)
 {
 	struct hci_conn *conn = data;
 
+	if (!hci_conn_valid(hdev, conn))
+		return;
+
 	bt_dev_dbg(hdev, "conn %p", conn);
 
 	if (err) {
-- 
2.43.0

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

end of thread, other threads:[~2026-03-04 21:32 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-04 20:59 [PATCH] Bluetooth: hci_conn: Fix UAF in create_big_sync and create_big_complete Kai Zen
2026-03-04 21:32 ` 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