]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Bluetooth: ISO: fix CONNECTED -> CLOSED transition on shutdown/release
authorPauli Virtanen <pav@iki.fi>
Fri, 24 Jul 2026 20:20:24 +0000 (23:20 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 28 Jul 2026 20:12:06 +0000 (16:12 -0400)
Commit d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
merged a version of the UAF fix that breaks releasing connected
ISO sockets. Since hci_conn::iso_data is set to NULL, iso_chan_del() won't
be called when the hci_conn disconnects, and the ISO socket does not emit
POLLHUP correctly.

Fix by retaining full hci_conn <-> iso_conn association while in
BT_DISCONNECT state, so that local disconnect via shutdown() follows
similar ISO socket code path as remote disconnect.  Use a separate flag
to track whether hci_conn_drop() is needed, instead of setting
iso_conn::hcon = NULL

In iso_sock_ready(), disallow disconnecting socket going BT_CONNECTED,
in case hcon connects while its drop is pending.

Fixes: d57e506f6a1e ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
Fixes: fbdc4bc47268 ("Bluetooth: ISO: Use defer setup to separate PA sync and BIG sync")
Signed-off-by: Pauli Virtanen <pav@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/iso.c

index babba61eb33541f33ea280cee00f50f2e2ef0c7b..299a9336b5e117fa612cf1dd64e0ede0a7dd8f97 100644 (file)
@@ -24,8 +24,14 @@ static struct bt_sock_list iso_sk_list = {
 };
 
 /* ---- ISO connections ---- */
+enum {
+       ISO_CONN_DROPPED,
+       __ISO_CONN_NUM_FLAGS
+};
+
 struct iso_conn {
        struct hci_conn *hcon;
+       DECLARE_BITMAP(flags, __ISO_CONN_NUM_FLAGS);
 
        /* @lock: spinlock protecting changes to iso_conn fields */
        spinlock_t      lock;
@@ -107,7 +113,8 @@ static void iso_conn_free(struct kref *ref)
 
        if (conn->hcon) {
                conn->hcon->iso_data = NULL;
-               hci_conn_drop(conn->hcon);
+               if (!test_and_set_bit(ISO_CONN_DROPPED, conn->flags))
+                       hci_conn_drop(conn->hcon);
        }
 
        /* Ensure no more work items will run since hci_conn has been dropped */
@@ -306,6 +313,7 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
 
        iso_pi(sk)->conn = conn;
        conn->sk = sk;
+       clear_bit(ISO_CONN_DROPPED, conn->flags);
 
        if (parent)
                bt_accept_enqueue(parent, sk, true);
@@ -835,11 +843,8 @@ static void iso_sock_disconn(struct sock *sk)
        }
 
        sk->sk_state = BT_DISCONN;
-       iso_conn_lock(iso_pi(sk)->conn);
-       hci_conn_drop(iso_pi(sk)->conn->hcon);
-       iso_pi(sk)->conn->hcon->iso_data = NULL;
-       iso_pi(sk)->conn->hcon = NULL;
-       iso_conn_unlock(iso_pi(sk)->conn);
+       if (!test_and_set_bit(ISO_CONN_DROPPED, iso_pi(sk)->conn->flags))
+               hci_conn_drop(iso_pi(sk)->conn->hcon);
 }
 
 static void __iso_sock_close(struct sock *sk)
@@ -2042,9 +2047,18 @@ static void iso_sock_ready(struct sock *sk)
                return;
 
        lock_sock(sk);
+
+       switch (sk->sk_state) {
+       case BT_DISCONN:
+       case BT_CLOSED:
+               release_sock(sk);
+               return;
+       }
+
        iso_sock_clear_timer(sk);
        sk->sk_state = BT_CONNECTED;
        sk->sk_state_change(sk);
+
        release_sock(sk);
 }