Re: [PATCH] Bluetooth: hci_sync: Fix scan response data UAF

Luiz Augusto von Dentz <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <CABBYNZ+G3jD3A6-k1fEm2sSRa27o8s0PNN_arzVa8de-eJK8oQ@mail.gmail.com>
Hi,

On Mon, Jul 20, 2026 at 12:47 PM Chengfeng Ye <[email protected]> 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: [email protected]
> Signed-off-by: Chengfeng Ye <[email protected]>
> ---
>  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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.