Re: [PATCH] Bluetooth: hci_sync: Fix accept list UAF during suspend
Luiz Augusto von Dentz <[email protected]> Fri, 31 Jul 2026 15:45:12 -0400
| Newsgroups | org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel,org.kernel.vger.stable |
|---|---|
| Message-ID | <CABBYNZKgxz4JbKv8fgTeWGEGV0P_pPmcy_YG=rLDroc_1wu==g@mail.gmail.com> |
Hi Chengfeng, On Thu, Jul 30, 2026 at 5:23=E2=80=AFAM Chengfeng Ye <[email protected]= > wrote: > > 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->loc= k. > 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. This preserves list order and avoids > retaining an accept-list node across a controller wait. > > Fixes: 182ee45da083 ("Bluetooth: hci_sync: Rework hci_suspend_notifier") > Cc: [email protected] > Signed-off-by: Chengfeng Ye <[email protected]> > --- > net/bluetooth/hci_sync.c | 38 +++++++++++++++++++++++++++++++------- > 1 file changed, 31 insertions(+), 7 deletions(-) > > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c > index c0b1fc293b49..540da19d1d64 100644 > --- a/net/bluetooth/hci_sync.c > +++ b/net/bluetooth/hci_sync.c > @@ -6250,6 +6250,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 =3D NULL; > + size_t i, num_entries =3D 0; > u8 scan =3D SCAN_DISABLED; > bool scanning =3D test_bit(HCI_PSCAN, &hdev->flags); > int err; > @@ -6263,26 +6265,48 @@ static int hci_update_event_filter_sync(struct hc= i_dev *hdev) > if (hci_test_quirk(hdev, HCI_QUIRK_BROKEN_FILTER_CLEAR_ALL)) > return 0; > > + hci_dev_lock(hdev); > + > + list_for_each_entry(b, &hdev->accept_list, list) > + if (b->flags & HCI_CONN_FLAG_REMOTE_WAKEUP) > + num_entries++; If !num_entries we can probably bail out here, or we do need to call hci_clear_event_filter_sync? If so, perhaps that should be called first. > + if (num_entries) { > + accept_list =3D kmalloc_array(num_entries, sizeof(*accept= _list), > + GFP_KERNEL); > + if (!accept_list) { > + hci_dev_unlock(hdev); > + return -ENOMEM; > + } > + } > + > + i =3D 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); While this works, iterating twice if there are no entries is wasteful; we should bail out or rework this. > /* 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; > - > - bt_dev_dbg(hdev, "Adding event filters for %pMR", &b->bda= ddr); > + for (i =3D 0; i < num_entries; i++) { > + bt_dev_dbg(hdev, "Adding event filters for %pMR", > + &accept_list[i]); > > err =3D hci_set_event_filter_sync(hdev, HCI_FLT_CONN_SET= UP, > HCI_CONN_SETUP_ALLOW_BDA= DDR, > - &b->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 =3D SCAN_PAGE; > } > > + kfree(accept_list); > + > if (scan && !scanning) > hci_write_scan_enable_sync(hdev, scan); > else if (!scan && scanning) > -- > 2.43.0 > --=20 Luiz Augusto von Dentz