[PATCH 09/11] Bluetooth: ISO: ensure no dangling hcon references in iso_conn

From: Pauli Virtanen

Date: Fri Jul 24 2026 - 16:27:16 EST


After iso_conn_del(), ISO sockets should not dereference the hcon any
more. Currently, clearing iso_conn::hcon relies on iso_conn_del()
releasing the last reference to the iso_conn.

Simplify this by explicitly clearing conn->hcon in iso_conn_del(), to
avoid more complex reasoning on races about who holds the last
reference.

Signed-off-by: Pauli Virtanen <pav@xxxxxx>
---
net/bluetooth/iso.c | 24 +++++++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 0cc08416fde5..bfd39baa8503 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -263,6 +263,7 @@ static void iso_chan_del(struct sock *sk, int err)
}

static void iso_conn_del(struct hci_conn *hcon, int err)
+ __must_hold(&hcon->hdev->lock)
{
struct iso_conn *conn = hcon->iso_data;
struct sock *sk;
@@ -277,11 +278,10 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
iso_conn_lock(conn);
sk = iso_sock_hold(conn);
iso_conn_unlock(conn);
- iso_conn_put(conn);

if (!sk) {
iso_conn_put(conn);
- return;
+ goto done;
}

iso_sock_disable_timer(sk);
@@ -291,6 +291,15 @@ static void iso_conn_del(struct hci_conn *hcon, int err)
release_sock(sk);
iso_sock_kill(sk);
sock_put(sk);
+
+done:
+ /* No sk access to conn->hcon any more (lock_sock + hdev->lock) */
+ iso_conn_lock(conn);
+ conn->hcon = NULL;
+ hcon->iso_data = NULL;
+ iso_conn_unlock(conn);
+
+ iso_conn_put(conn);
}

static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
@@ -306,6 +315,11 @@ static int __iso_chan_add(struct iso_conn *conn, struct sock *sk,
return -EBUSY;
}

+ if (!conn->hcon) {
+ BT_ERR("conn->hcon missing");
+ return -EIO;
+ }
+
iso_pi(sk)->conn = conn;
conn->sk = sk;
clear_bit(ISO_CONN_DROPPED, conn->flags);
@@ -2500,6 +2514,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
}

static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK) {
@@ -2511,8 +2526,10 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
struct hci_link *link, *t;

list_for_each_entry_safe(link, t, &hcon->link_list,
- list)
+ list) {
+ lockdep_assert_held(&link->conn->hdev->lock);
iso_conn_del(link->conn, bt_to_errno(status));
+ }

return;
}
@@ -2542,6 +2559,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
}

static void iso_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+ __must_hold(&hcon->hdev->lock)
{
if (hcon->type != CIS_LINK && hcon->type != BIS_LINK &&
hcon->type != PA_LINK)
--
2.55.0