]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
Bluetooth: hci_event: Fix using rcu_read_(un)lock while iterating
authorLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Wed, 4 Dec 2024 16:40:59 +0000 (11:40 -0500)
committerLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Thu, 12 Dec 2024 14:23:49 +0000 (09:23 -0500)
The usage of rcu_read_(un)lock while inside list_for_each_entry_rcu is
not safe since for the most part entries fetched this way shall be
treated as rcu_dereference:

Note that the value returned by rcu_dereference() is valid
only within the enclosing RCU read-side critical section [1]_.
For example, the following is **not** legal::

rcu_read_lock();
p = rcu_dereference(head.next);
rcu_read_unlock();
x = p->address; /* BUG!!! */
rcu_read_lock();
y = p->data; /* BUG!!! */
rcu_read_unlock();

Fixes: a0bfde167b50 ("Bluetooth: ISO: Add support for connecting multiple BISes")
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
net/bluetooth/hci_event.c

index 1427d6e2f3c9dafeb647f20dfd07c2ff9729bf5e..2cc7a93063501c327d623175d1e3fe3c9c3fa0c8 100644 (file)
@@ -6870,38 +6870,27 @@ static void hci_le_create_big_complete_evt(struct hci_dev *hdev, void *data,
                return;
 
        hci_dev_lock(hdev);
-       rcu_read_lock();
 
        /* Connect all BISes that are bound to the BIG */
-       list_for_each_entry_rcu(conn, &hdev->conn_hash.list, list) {
-               if (bacmp(&conn->dst, BDADDR_ANY) ||
-                   conn->type != ISO_LINK ||
-                   conn->iso_qos.bcast.big != ev->handle)
+       while ((conn = hci_conn_hash_lookup_big_state(hdev, ev->handle,
+                                                     BT_BOUND))) {
+               if (ev->status) {
+                       hci_connect_cfm(conn, ev->status);
+                       hci_conn_del(conn);
                        continue;
+               }
 
                if (hci_conn_set_handle(conn,
                                        __le16_to_cpu(ev->bis_handle[i++])))
                        continue;
 
-               if (!ev->status) {
-                       conn->state = BT_CONNECTED;
-                       set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
-                       rcu_read_unlock();
-                       hci_debugfs_create_conn(conn);
-                       hci_conn_add_sysfs(conn);
-                       hci_iso_setup_path(conn);
-                       rcu_read_lock();
-                       continue;
-               }
-
-               hci_connect_cfm(conn, ev->status);
-               rcu_read_unlock();
-               hci_conn_del(conn);
-               rcu_read_lock();
+               conn->state = BT_CONNECTED;
+               set_bit(HCI_CONN_BIG_CREATED, &conn->flags);
+               hci_debugfs_create_conn(conn);
+               hci_conn_add_sysfs(conn);
+               hci_iso_setup_path(conn);
        }
 
-       rcu_read_unlock();
-
        if (!ev->status && !i)
                /* If no BISes have been connected for the BIG,
                 * terminate. This is in case all bound connections