]> git.hungrycats.org Git - linux/commitdiff
Bluetooth: hci_sync: Fix accept list UAF during suspend
authorChengfeng Ye <nicoyip.dev@gmail.com>
Sat, 1 Aug 2026 07:05:24 +0000 (15:05 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 27 Aug 2026 12:32:54 +0000 (14:32 +0200)
commit f57b399c4fa1501b2d5451f52d861ece86bcf3db upstream.

hci_update_event_filter_sync() walks hdev->accept_list while sending a
synchronous HCI command for each remote-wakeup device.  The suspend path
holds hdev->req_lock, but accept-list updates are serialized by hdev->lock.
Consequently, remove_device() can free the current list entry during the
controller wait.

The following interleaving causes the use-after-free:

  hci_update_event_filter_sync()    remove_device()
  fetch accept-list entry
  hci_set_event_filter_sync()
    wait for controller response    hci_dev_lock()
                                    list_del()
                                    kfree()
                                    hci_dev_unlock()
  read the freed list.next

KASAN reported:

  BUG: KASAN: slab-use-after-free in hci_suspend_sync+0x835/0x910
  Read of size 8 at addr ffff88810bec8440 by task kworker/0:1/10
  Workqueue: events vhci_suspend_work
  Call Trace:
   hci_suspend_sync+0x835/0x910
   hci_suspend_dev+0x182/0x450
   process_one_work+0x661/0x1090
   worker_thread+0x45b/0xd10

  Allocated by task 86:
   hci_bdaddr_list_add_with_flags+0x1a8/0x400
   add_device+0x381/0x820
   hci_sock_sendmsg+0x1033/0x1ea0

  Freed by task 91:
   kfree+0x131/0x3c0
   remove_device+0x429/0xb70
   hci_sock_sendmsg+0x1033/0x1ea0

Snapshot the remote-wakeup addresses under hdev->lock.  Release the lock
before sending HCI commands.  Clear the controller event filter before
building the snapshot, and skip allocation and the second list traversal
when there are no matching entries.  This preserves the original filter
and scan-state updates without retaining an accept-list node across a
controller wait.

Fixes: 182ee45da083 ("Bluetooth: hci_sync: Rework hci_suspend_notifier")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/linux-bluetooth/20260730092331.2069741-1-nicoyip.dev@gmail.com/
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/bluetooth/hci_sync.c

index 580b4a8583d507cb8fa585a304feacb92b1b19d2..5c7cd2a38d510a8390b0177a564acacca92cb856 100644 (file)
@@ -6239,6 +6239,8 @@ static int hci_pause_discovery_sync(struct hci_dev *hdev)
 static int hci_update_event_filter_sync(struct hci_dev *hdev)
 {
        struct bdaddr_list_with_flags *b;
+       bdaddr_t *accept_list;
+       size_t i, num_entries = 0;
        u8 scan = SCAN_DISABLED;
        bool scanning = test_bit(HCI_PSCAN, &hdev->flags);
        int err;
@@ -6255,23 +6257,49 @@ static int hci_update_event_filter_sync(struct hci_dev *hdev)
        /* Always clear event filter when starting */
        hci_clear_event_filter_sync(hdev);
 
-       list_for_each_entry(b, &hdev->accept_list, list) {
-               if (!(b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP))
-                       continue;
+       hci_dev_lock(hdev);
+
+       list_for_each_entry(b, &hdev->accept_list, list)
+               if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
+                       num_entries++;
 
-               bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bdaddr);
+       if (!num_entries) {
+               hci_dev_unlock(hdev);
+               goto update_scan;
+       }
 
-               err =  hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
-                                                HCI_CONN_SETUP_ALLOW_BDADDR,
-                                                &b->bdaddr,
-                                                HCI_CONN_SETUP_AUTO_ON);
+       accept_list = kmalloc_array(num_entries, sizeof(*accept_list),
+                                   GFP_KERNEL);
+       if (!accept_list) {
+               hci_dev_unlock(hdev);
+               return -ENOMEM;
+       }
+
+       i = 0;
+       list_for_each_entry(b, &hdev->accept_list, list)
+               if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP)
+                       bacpy(&accept_list[i++], &b->bdaddr);
+
+       hci_dev_unlock(hdev);
+
+       for (i = 0; i < num_entries; i++) {
+               bt_dev_dbg(hdev, "Adding event filters for %pMR",
+                          &accept_list[i]);
+
+               err = hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SETUP,
+                                               HCI_CONN_SETUP_ALLOW_BDADDR,
+                                        &accept_list[i],
+                                        HCI_CONN_SETUP_AUTO_ON);
                if (err)
                        bt_dev_err(hdev, "Failed to set event filter for %pMR",
-                                  &b->bdaddr);
+                                  &accept_list[i]);
                else
                        scan = SCAN_PAGE;
        }
 
+       kfree(accept_list);
+
+update_scan:
        if (scan && !scanning)
                hci_write_scan_enable_sync(hdev, scan);
        else if (!scan && scanning)