Re: [PATCH] Bluetooth: hci_sync: Fix scan response data UAF
From: Luiz Augusto von Dentz
Date: Mon Jul 20 2026 - 15:59:40 EST
Hi,
On Mon, Jul 20, 2026 at 12:47 PM Chengfeng Ye <nicoyip.dev@xxxxxxxxx> wrote:
>
> eir_create_scan_rsp() calls hci_find_adv_instance(), whose return value is
> valid only while hdev->lock is held. After advertising setup was moved to
> hci_cmd_sync_work, neither scan response data builder held that lock.
> hci_set_ext_scan_rsp_data_sync() also kept the returned pointer while
> waiting for the controller and wrote scan_rsp_changed through it afterward.
>
> An advertising termination event can therefore interleave as follows:
>
> hci_cmd_sync_work hci_rx_work
> hci_find_adv_instance()
> __hci_cmd_sync_status()
> wait for controller reply hci_dev_lock()
> hci_remove_adv_instance()
> kfree(adv)
> adv->scan_rsp_changed = false
>
> KASAN reported:
>
> BUG: KASAN: slab-use-after-free in hci_set_ext_scan_rsp_data_sync+0x2e1/0x300
> Write of size 1 at addr ffff88810a45d21d by task kworker/u17:0/88
> Workqueue: hci0 hci_cmd_sync_work
> Call Trace:
> hci_set_ext_scan_rsp_data_sync+0x2e1/0x300
> hci_schedule_adv_instance_sync+0x390/0x4c0
> hci_cmd_sync_work+0x173/0x300
> Allocated by task 87:
> hci_add_adv_instance+0x538/0xac0
> add_advertising+0x885/0x1160
> Freed by task 89:
> kfree+0x131/0x3c0
> hci_remove_adv_instance+0x1d8/0x3b0
> hci_le_ext_adv_term_evt+0x17b/0x730
>
> Hold hdev->lock while looking up the instance, building the scan
> response, and clearing its dirty bit. Clear the bit before waiting for
> the controller so no adv_info pointer survives the wait, and restore it
> under the lock if the command fails. This also preserves an update that
> races with a successful command because that update sets the bit again.
> Protect the legacy scan response builder with the same lock.
>
> Fixes: cba6b758711c ("Bluetooth: hci_sync: Make use of hci_cmd_sync_queue set 2")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Chengfeng Ye <nicoyip.dev@xxxxxxxxx>
> ---
> net/bluetooth/hci_sync.c | 32 +++++++++++++++++++++++++++-----
> 1 file changed, 27 insertions(+), 5 deletions(-)
>
> diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> index 532534bc601c..8c69a98c71e8 100644
> --- a/net/bluetooth/hci_sync.c
> +++ b/net/bluetooth/hci_sync.c
> @@ -1490,9 +1490,13 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
> int err;
>
> if (instance) {
> + hci_dev_lock(hdev);
> +
> adv = hci_find_adv_instance(hdev, instance);
> - if (!adv || !adv->scan_rsp_changed)
> + if (!adv || !adv->scan_rsp_changed) {
> + hci_dev_unlock(hdev);
> return 0;
> + }
> }
>
> len = eir_create_scan_rsp(hdev, instance, pdu->data);
> @@ -1502,15 +1506,27 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
> pdu->operation = LE_SET_ADV_DATA_OP_COMPLETE;
> pdu->frag_pref = LE_SET_ADV_DATA_NO_FRAG;
>
> + if (adv) {
> + adv->scan_rsp_changed = false;
> + hci_dev_unlock(hdev);
> + }
> +
> err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
> struct_size(pdu, data, len), pdu,
> HCI_CMD_TIMEOUT);
> - if (err)
> + if (err) {
> + if (instance) {
> + hci_dev_lock(hdev);
> + adv = hci_find_adv_instance(hdev, instance);
> + if (adv)
> + adv->scan_rsp_changed = true;
> + hci_dev_unlock(hdev);
> + }
> +
> return err;
> + }
>
> - if (adv) {
> - adv->scan_rsp_changed = false;
> - } else {
> + if (!instance) {
> memcpy(hdev->scan_rsp_data, pdu->data, len);
> hdev->scan_rsp_data_len = len;
> }
> @@ -1525,8 +1541,14 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
>
> memset(&cp, 0, sizeof(cp));
>
> + if (instance)
> + hci_dev_lock(hdev);
> +
> len = eir_create_scan_rsp(hdev, instance, cp.data);
>
> + if (instance)
> + hci_dev_unlock(hdev);
> +
> if (hdev->scan_rsp_data_len == len &&
> !memcmp(cp.data, hdev->scan_rsp_data, len))
> return 0;
> --
> 2.43.0
It looks like Sashiko found some other places where this could be a problem:
https://sashiko.dev/#/patchset/20260720164738.2921802-1-nicoyip.dev%40gmail.com
--
Luiz Augusto von Dentz