Skip to content

Commit

Permalink
Bluetooth: qca: add missing firmware sanity checks
Browse files Browse the repository at this point in the history
commit 2e4edfa upstream.

Add the missing sanity checks when parsing the firmware files before
downloading them to avoid accessing and corrupting memory beyond the
vmalloced buffer.

Fixes: 83e8196 ("Bluetooth: btqca: Introduce generic QCA ROME support")
Cc: [email protected]	# 4.10
Signed-off-by: Johan Hovold <[email protected]>
Signed-off-by: Luiz Augusto von Dentz <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
jhovold authored and gregkh committed May 17, 2024
1 parent d68dbfb commit ed53949
Showing 1 changed file with 32 additions and 6 deletions.
38 changes: 32 additions & 6 deletions drivers/bluetooth/btqca.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
}
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);

static void qca_tlv_check_data(struct hci_dev *hdev,
static int qca_tlv_check_data(struct hci_dev *hdev,
struct qca_fw_config *config,
u8 *fw_data, enum qca_btsoc_type soc_type)
u8 *fw_data, size_t fw_size,
enum qca_btsoc_type soc_type)
{
const u8 *data;
u32 type_len;
Expand All @@ -200,6 +201,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,

switch (config->type) {
case ELF_TYPE_PATCH:
if (fw_size < 7)
return -EINVAL;

config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
config->dnld_type = QCA_SKIP_EVT_VSE_CC;

Expand All @@ -208,6 +212,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
break;
case TLV_TYPE_PATCH:
if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
return -EINVAL;

tlv = (struct tlv_type_hdr *)fw_data;
type_len = le32_to_cpu(tlv->type_len);
tlv_patch = (struct tlv_type_patch *)tlv->data;
Expand Down Expand Up @@ -247,6 +254,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;

case TLV_TYPE_NVM:
if (fw_size < sizeof(struct tlv_type_hdr))
return -EINVAL;

tlv = (struct tlv_type_hdr *)fw_data;

type_len = le32_to_cpu(tlv->type_len);
Expand All @@ -255,17 +265,26 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
BT_DBG("Length\t\t : %d bytes", length);

if (fw_size < length + (tlv->data - fw_data))
return -EINVAL;

idx = 0;
data = tlv->data;
while (idx < length) {
while (idx < length - sizeof(struct tlv_type_nvm)) {
tlv_nvm = (struct tlv_type_nvm *)(data + idx);

tag_id = le16_to_cpu(tlv_nvm->tag_id);
tag_len = le16_to_cpu(tlv_nvm->tag_len);

if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
return -EINVAL;

/* Update NVM tags as needed */
switch (tag_id) {
case EDL_TAG_ID_HCI:
if (tag_len < 3)
return -EINVAL;

/* HCI transport layer parameters
* enabling software inband sleep
* onto controller side.
Expand All @@ -281,6 +300,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;

case EDL_TAG_ID_DEEP_SLEEP:
if (tag_len < 1)
return -EINVAL;

/* Sleep enable mask
* enabling deep sleep feature on controller.
*/
Expand All @@ -289,14 +311,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
break;
}

idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
idx += sizeof(struct tlv_type_nvm) + tag_len;
}
break;

default:
BT_ERR("Unknown TLV type %d", config->type);
break;
return -EINVAL;
}

return 0;
}

static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
Expand Down Expand Up @@ -446,7 +470,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
memcpy(data, fw->data, size);
release_firmware(fw);

qca_tlv_check_data(hdev, config, data, soc_type);
ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
if (ret)
return ret;

segment = data;
remain = size;
Expand Down

0 comments on commit ed53949

Please sign in to comment.