Re: [BUG] Bluetooth: stack out-of-bounds write in eir_create_adv_data() and eir_create_scan_rsp() when a controller stops reporting LE Extended Advertising (seen on 7.3.1)
From: Piyus Kedia
Date: Tue Nov 10 2026 - 13:43:06 EST
Hi Luiz,
Thanks for the review. v2 is on the list, doing what you suggested:
unregistering the oversized instances at power-on rather than bounding
the copy in eir.c, with MGMT_EV_ADVERTISING_REMOVED so userspace is
told. It is also sent as a proper patch this time, with a Fixes: tag,
and verified at runtime.
https://lore.kernel.org/linux-bluetooth/20261110175334.676816-1-piyus@xxxxxxxxxxx/T/#t
On the security@xxxxxxxxxx point: you are right, and I withdraw the
claim. Posting it to the public list ended any embargo, so this is an
ordinary bug report and should be handled as one. I have not copied
security@xxxxxxxxxx on v2 and will not pursue it there.
Thanks and regards,
Piyus Kedia
Kapil Vaswani
On Tue, Nov 10, 2026 at 8:41 PM Luiz Augusto von Dentz
<luiz.dentz@xxxxxxxxx> wrote:
>
> Hi Piyus,
>
> On Mon, Nov 9, 2026 at 11:57 PM Piyus Kedia <piyus@xxxxxxxxxxx> wrote:
> > diff --git a/net/bluetooth/eir.c b/net/bluetooth/eir.c
> >
> > --- a/net/bluetooth/eir.c
> >
> > +++ b/net/bluetooth/eir.c
> >
> > @@ -300,6 +300,16 @@ u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size)
> >
> >
> >
> > skip_flags:
> >
> > if (adv) {
> >
> > + /* The instance's length was validated against max_adv_len()
> >
> > + * when it was set, and max_adv_len() depends on a controller
> >
> > + * capability that is re-read on every bring-up - so by now it
> >
> > + * can be larger than the caller's buffer. Refuse rather than
> >
> > + * overflow; a truncated AD structure would not be parseable
> >
> > + * by a peer anyway.
> >
> > + */
> >
> > + if (ad_len + adv->adv_data_len > size)
> >
> > + return 0;
> >
> > +
>
> We probably need to unregister the advertising instances if the
> controller changes its max size.
>
> > memcpy(ptr, adv->adv_data, adv->adv_data_len);
> >
> > ad_len += adv->adv_data_len;
> >
> > ptr += adv->adv_data_len;
> >
> > @@ -342,7 +352,7 @@ static u8 create_default_scan_rsp(struct hci_dev *hdev, u8 *ptr)
> >
> > return eir_append_local_name(hdev, ptr, scan_rsp_len);
> >
> > }
> >
> >
> >
> > -u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr)
> >
> > +u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size)
> >
> > {
> >
> > struct adv_info *adv;
> >
> > u8 scan_rsp_len = 0;
> >
> > @@ -357,6 +367,10 @@ u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr)
> >
> > if ((adv->flags & MGMT_ADV_FLAG_APPEARANCE) && hdev->appearance)
> >
> > scan_rsp_len = eir_append_appearance(hdev, ptr, scan_rsp_len);
> >
> >
> >
> > + /* See the matching check in eir_create_adv_data(). */
> >
> > + if (scan_rsp_len + adv->scan_rsp_len > size)
> >
> > + return 0;
> >
> > +
> >
> > memcpy(&ptr[scan_rsp_len], adv->scan_rsp_data, adv->scan_rsp_len);
> >
> >
> >
> > scan_rsp_len += adv->scan_rsp_len;
> >
> > diff --git a/net/bluetooth/eir.h b/net/bluetooth/eir.h
> >
> > --- a/net/bluetooth/eir.h
> >
> > +++ b/net/bluetooth/eir.h
> >
> > @@ -10,7 +10,7 @@
> >
> > void eir_create(struct hci_dev *hdev, u8 *data);
> >
> >
> >
> > u8 eir_create_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size);
> >
> > -u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr);
> >
> > +u8 eir_create_scan_rsp(struct hci_dev *hdev, u8 instance, u8 *ptr, u8 size);
> >
> > u8 eir_create_per_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr);
> >
> >
> >
> > u8 eir_append_local_name(struct hci_dev *hdev, u8 *eir, u8 ad_len);
> >
> > diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
> >
> > --- a/net/bluetooth/hci_sync.c
> >
> > +++ b/net/bluetooth/hci_sync.c
> >
> > @@ -1563,7 +1563,8 @@ static int hci_set_ext_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
> >
> > }
> >
> > }
> >
> >
> >
> > - len = eir_create_scan_rsp(hdev, instance, pdu->data);
> >
> > + len = eir_create_scan_rsp(hdev, instance, pdu->data,
> >
> > + HCI_MAX_EXT_AD_LENGTH);
>
> Ditto
>
> >
> >
> > pdu->handle = adv ? adv->handle : instance;
> >
> > pdu->length = len;
> >
> > @@ -1609,7 +1610,7 @@ static int __hci_set_scan_rsp_data_sync(struct hci_dev *hdev, u8 instance)
> >
> > if (instance)
> >
> > hci_dev_lock(hdev);
> >
> >
> >
> > - len = eir_create_scan_rsp(hdev, instance, cp.data);
> >
> > + len = eir_create_scan_rsp(hdev, instance, cp.data, sizeof(cp.data));
>
> Ditto.
>
> >
> >
> > if (instance)
> >
> > hci_dev_unlock(hdev);
>
> Next time, send it as a proper patch, so CI can pick it up and Sashiko
> can analyze the changes. Also, and also address the claims about it
> being material to security@xxxxxxxxxx don't hold anymore since you
> just published it to linux-bluetooth.