]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
Bluetooth: hci_conn: Always allocate unique handles
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 28 Jun 2023 19:15:53 +0000 (12:15 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 Sep 2023 07:48:04 +0000 (09:48 +0200)
[ Upstream commit 9f78191cc9f1b34c2e2afd7b554a83bf034092dd ]

This attempts to always allocate a unique handle for connections so they
can be properly aborted by the likes of hci_abort_conn, so this uses the
invalid range as a pool of unset handles that way if userspace is trying
to create multiple connections at once each will be given a unique
handle which will be considered unset.

Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Stable-dep-of: 66dee21524d9 ("Bluetooth: hci_event: drop only unbound CIS if Set CIG Parameters fails")
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/bluetooth/hci_core.h
net/bluetooth/hci_conn.c
net/bluetooth/hci_event.c

index e30e091ef728e93c80d86ed53046cc39c78e6953..2db826cdf76fa2ddec685d3872b4b9a9926676c6 100644 (file)
@@ -321,8 +321,8 @@ struct adv_monitor {
 
 #define HCI_MAX_SHORT_NAME_LENGTH      10
 
-#define HCI_CONN_HANDLE_UNSET          0xffff
 #define HCI_CONN_HANDLE_MAX            0x0eff
+#define HCI_CONN_HANDLE_UNSET(_handle) (_handle > HCI_CONN_HANDLE_MAX)
 
 /* Min encryption key size to match with SMP */
 #define HCI_MIN_ENC_KEY_SIZE           7
index 7762161a3fc8b729b0c64494052aac0cc1db06e3..7ced6077488f7b3bbf115078d0cce94046684d45 100644 (file)
@@ -991,6 +991,25 @@ static void cis_cleanup(struct hci_conn *conn)
        hci_le_remove_cig(hdev, conn->iso_qos.ucast.cig);
 }
 
+static u16 hci_conn_hash_alloc_unset(struct hci_dev *hdev)
+{
+       struct hci_conn_hash *h = &hdev->conn_hash;
+       struct hci_conn  *c;
+       u16 handle = HCI_CONN_HANDLE_MAX + 1;
+
+       rcu_read_lock();
+
+       list_for_each_entry_rcu(c, &h->list, list) {
+               /* Find the first unused handle */
+               if (handle == 0xffff || c->handle != handle)
+                       break;
+               handle++;
+       }
+       rcu_read_unlock();
+
+       return handle;
+}
+
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
                              u8 role)
 {
@@ -1004,7 +1023,7 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
 
        bacpy(&conn->dst, dst);
        bacpy(&conn->src, &hdev->bdaddr);
-       conn->handle = HCI_CONN_HANDLE_UNSET;
+       conn->handle = hci_conn_hash_alloc_unset(hdev);
        conn->hdev  = hdev;
        conn->type  = type;
        conn->role  = role;
@@ -1117,7 +1136,7 @@ static void hci_conn_unlink(struct hci_conn *conn)
                         */
                        if ((child->type == SCO_LINK ||
                             child->type == ESCO_LINK) &&
-                           child->handle == HCI_CONN_HANDLE_UNSET)
+                           HCI_CONN_HANDLE_UNSET(child->handle))
                                hci_conn_del(child);
                }
 
@@ -1968,7 +1987,7 @@ int hci_conn_check_create_cis(struct hci_conn *conn)
                return -EINVAL;
 
        if (!conn->parent || conn->parent->state != BT_CONNECTED ||
-           conn->state != BT_CONNECT || conn->handle == HCI_CONN_HANDLE_UNSET)
+           conn->state != BT_CONNECT || HCI_CONN_HANDLE_UNSET(conn->handle))
                return 1;
 
        return 0;
index e3675d8a23e441b4c77597e6ec79f7b655ab1746..22fb9f9da866b6dc002f13a1a4744ea0bec9a49b 100644 (file)
@@ -3173,7 +3173,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, void *data,
         * As the connection handle is set here for the first time, it indicates
         * whether the connection is already set up.
         */
-       if (conn->handle != HCI_CONN_HANDLE_UNSET) {
+       if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
                bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection");
                goto unlock;
        }
@@ -5008,7 +5008,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, void *data,
         * As the connection handle is set here for the first time, it indicates
         * whether the connection is already set up.
         */
-       if (conn->handle != HCI_CONN_HANDLE_UNSET) {
+       if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
                bt_dev_err(hdev, "Ignoring HCI_Sync_Conn_Complete event for existing connection");
                goto unlock;
        }
@@ -5872,7 +5872,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
         * As the connection handle is set here for the first time, it indicates
         * whether the connection is already set up.
         */
-       if (conn->handle != HCI_CONN_HANDLE_UNSET) {
+       if (!HCI_CONN_HANDLE_UNSET(conn->handle)) {
                bt_dev_err(hdev, "Ignoring HCI_Connection_Complete for existing connection");
                goto unlock;
        }