Re: [PATCH v2] Bluetooth: btnxpuart: Fix out-of-bounds firmware read in nxp_recv_fw_req_v1()

Luiz Augusto von Dentz <[email protected]>
Newsgroups org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <CABBYNZLaFuoektaPQkzr4ioiz9_pUpbun1oow0kwgSOhbHM11g@mail.gmail.com>
Hi Doruk,

On Mon, Jul 20, 2026 at 3:13 PM Doruk Tan Ozturk <[email protected]> wrote:
>
> Commit 25c286d75821 ("Bluetooth: btnxpuart: Fix out-of-bounds firmware
> read in nxp_recv_fw_req_v3()") bounded the v3 firmware download offset but
> left an unbounded read in the v1 handler.
>
> nxp_recv_fw_req_v1() advances a device-driven download offset
> (fw_dnld_v1_offset) by fw_v1_sent_bytes on every request, and that
> bookkeeping runs even when the payload write is skipped, so the offset can
> walk past nxpdev->fw->size. When the controller then requests a header
> (len == HDR_LEN), the driver reads the 16-byte bootloader header at
>
>   nxp_get_data_len(nxpdev->fw->data + nxpdev->fw_dnld_v1_offset)
>
> with no bound on the offset, reading past the end of the firmware image.
> A malicious or malfunctioning NXP UART controller can drive this to read
> out-of-bounds kernel memory during firmware download.
>
> Bound the offset before the header read, and convert the payload write
> guard to the overflow-safe form used by the v3 path (fw_dnld_v1_offset is
> u32, so fw_dnld_v1_offset + len can wrap). Log the offset, expected length
> and firmware size at the reject site to aid debugging of a real device.
>
> This was found by 0sec automated security-research tooling
> (https://0sec.ai).
>
> Fixes: 689ca16e5232 ("Bluetooth: NXP: Add protocol support for NXP Bluetooth chipsets")
> Cc: [email protected]
> Reviewed-by: Neeraj Sanjay Kale <[email protected]>
> Assisted-by: 0sec:claude-opus-4-8
> Signed-off-by: Doruk Tan Ozturk <[email protected]>
> ---
> v2: log offset/expected_len/fw size at the reject site (requested by
>     Neeraj Kale and Paul Menzel).  No functional change to the bound
>     itself vs v1.
>
> v1: https://lore.kernel.org/linux-bluetooth/[email protected]/
>  drivers/bluetooth/btnxpuart.c | 15 ++++++++++++---
>  1 file changed, 12 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
> index 0bb300eef157..6d62d07a542b 100644
> --- a/drivers/bluetooth/btnxpuart.c
> +++ b/drivers/bluetooth/btnxpuart.c
> @@ -1041,11 +1041,19 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
>                  * and we need to re-send the previous header again.
>                  */
>                 if (len == nxpdev->fw_v1_expected_len) {
> -                       if (len == HDR_LEN)
> +                       if (len == HDR_LEN) {
> +                               if (nxpdev->fw_dnld_v1_offset >= nxpdev->fw->size ||
> +                                   nxpdev->fw->size - nxpdev->fw_dnld_v1_offset < HDR_LEN) {
> +                                       bt_dev_err(hdev, "FW request out of bounds: offset %u, expected_len %u, fw size %zu",

We still have the 80 columns limit in Bluetooth.

> +                                                  nxpdev->fw_dnld_v1_offset, len,
> +                                                  nxpdev->fw->size);
> +                                       goto free_skb;
> +                               }
>                                 nxpdev->fw_v1_expected_len = nxp_get_data_len(nxpdev->fw->data +
>                                                                         nxpdev->fw_dnld_v1_offset);
> -                       else
> +                       } else {
>                                 nxpdev->fw_v1_expected_len = HDR_LEN;
> +                       }
>                 } else if (len == HDR_LEN) {
>                         /* FW download out of sync. Send previous chunk again */
>                         nxpdev->fw_dnld_v1_offset -= nxpdev->fw_v1_sent_bytes;
> @@ -1053,7 +1061,8 @@ static int nxp_recv_fw_req_v1(struct hci_dev *hdev, struct sk_buff *skb)
>                 }
>         }
>
> -       if (nxpdev->fw_dnld_v1_offset + len <= nxpdev->fw->size)
> +       if (nxpdev->fw_dnld_v1_offset < nxpdev->fw->size &&
> +           len <= nxpdev->fw->size - nxpdev->fw_dnld_v1_offset)
>                 serdev_device_write_buf(nxpdev->serdev, nxpdev->fw->data +
>                                         nxpdev->fw_dnld_v1_offset, len);
>         nxpdev->fw_v1_sent_bytes = len;
> --
> 2.43.0
>


-- 
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.