]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Bluetooth: ISO: ensure no dangling hcon references in iso_conn
authorPauli Virtanen <pav@iki.fi>
Fri, 24 Jul 2026 20:20:32 +0000 (23:20 +0300)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Tue, 28 Jul 2026 20:13:12 +0000 (16:13 -0400)
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@iki.fi>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/iso.c

index 0cc08416fde5635383c563e085abeb37ca422770..bfd39baa8503d3a1db97d246477dc8974117020a 100644 (file)
@@ -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 @@ done:
 }
 
 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)