diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c index 27111ce8aee1..d200faf285d0 100644 --- a/drivers/bluetooth/btmtk.c +++ b/drivers/bluetooth/btmtk.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -59,6 +60,67 @@ struct btmtk_section_map { }; } __packed; +/* Reject a firmware image whose header, global descriptor or section map + * array does not fit in the file, and hand back the validated section + * count, so that the section map walks stay inside the firmware buffer. + */ +static int btmtk_fw_validate_layout(struct hci_dev *hdev, + const struct firmware *fw, + u32 *section_num) +{ + const struct btmtk_global_desc *globaldesc; + size_t need; + + if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) { + bt_dev_err(hdev, "Firmware too small: %zu bytes, need at least %u", + fw->size, + MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE); + return -EINVAL; + } + + globaldesc = (const struct btmtk_global_desc *)(fw->data + + MTK_FW_ROM_PATCH_HEADER_SIZE); + *section_num = le32_to_cpu(globaldesc->section_num); + + if (!*section_num) { + bt_dev_err(hdev, "Firmware declares no sections"); + return -EINVAL; + } + + if (check_mul_overflow((size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE, + (size_t)*section_num, &need) || + check_add_overflow(need, + (size_t)(MTK_FW_ROM_PATCH_HEADER_SIZE + + MTK_FW_ROM_PATCH_GD_SIZE), + &need)) { + bt_dev_err(hdev, "Firmware section count too large: %u", + *section_num); + return -EINVAL; + } + + if (fw->size < need) { + bt_dev_err(hdev, "Firmware truncated: %zu bytes, need %zu for %u sections", + fw->size, need, *section_num); + return -EINVAL; + } + + return 0; +} + +/* Reject a section whose payload does not lie within the firmware file. */ +static int btmtk_fw_validate_section(struct hci_dev *hdev, + const struct firmware *fw, int index, + u32 offset, u32 size) +{ + if (offset > fw->size || size > fw->size - offset) { + bt_dev_err(hdev, "Firmware section %d out of bounds: offset=%u, size=%u, fw_size=%zu", + index, offset, size, fw->size); + return -EINVAL; + } + + return 0; +} + static void btmtk_coredump(struct hci_dev *hdev) { int err; @@ -137,7 +199,6 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, { struct btmtk_hci_wmt_params wmt_params; struct btmtk_patch_header *hdr; - struct btmtk_global_desc *globaldesc = NULL; struct btmtk_section_map *sectionmap; const struct firmware *fw; const u8 *fw_ptr; @@ -153,11 +214,13 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, return err; } + err = btmtk_fw_validate_layout(hdev, fw, §ion_num); + if (err < 0) + goto err_release_fw; + fw_ptr = fw->data; fw_bin_ptr = fw_ptr; hdr = (struct btmtk_patch_header *)fw_ptr; - globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE); - section_num = le32_to_cpu(globaldesc->section_num); bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s", le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime); @@ -180,6 +243,11 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname, continue; if (dl_size > 0) { + err = btmtk_fw_validate_section(hdev, fw, i, + section_offset, dl_size); + if (err < 0) + goto err_release_fw; + retry = 20; while (retry > 0) { cmd[0] = 0; /* 0 means legacy dl mode. */