Bluetooth: ISO: fix CONNECTED -> CLOSED transition on shutdown/release

Commit d57e506f6a ("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: d57e506f6a ("Bluetooth: ISO: clear iso_data always when detaching conn from hcon")
Fixes: fbdc4bc472 ("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>
This commit is contained in:
Pauli Virtanen 2026-07-24 23:20:24 +03:00 committed by Luiz Augusto von Dentz
parent cdc36db204
commit 0786469ee2

View 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);
}