Skip to content

Commit 2e4edfa

Browse files
jhovoldVudentz
authored andcommitted
Bluetooth: qca: add missing firmware sanity checks
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]>
1 parent 10f9f42 commit 2e4edfa

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

drivers/bluetooth/btqca.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,10 @@ int qca_send_pre_shutdown_cmd(struct hci_dev *hdev)
268268
}
269269
EXPORT_SYMBOL_GPL(qca_send_pre_shutdown_cmd);
270270

271-
static void qca_tlv_check_data(struct hci_dev *hdev,
271+
static int qca_tlv_check_data(struct hci_dev *hdev,
272272
struct qca_fw_config *config,
273-
u8 *fw_data, enum qca_btsoc_type soc_type)
273+
u8 *fw_data, size_t fw_size,
274+
enum qca_btsoc_type soc_type)
274275
{
275276
const u8 *data;
276277
u32 type_len;
@@ -286,6 +287,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
286287

287288
switch (config->type) {
288289
case ELF_TYPE_PATCH:
290+
if (fw_size < 7)
291+
return -EINVAL;
292+
289293
config->dnld_mode = QCA_SKIP_EVT_VSE_CC;
290294
config->dnld_type = QCA_SKIP_EVT_VSE_CC;
291295

@@ -294,6 +298,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
294298
bt_dev_dbg(hdev, "File version : 0x%x", fw_data[6]);
295299
break;
296300
case TLV_TYPE_PATCH:
301+
if (fw_size < sizeof(struct tlv_type_hdr) + sizeof(struct tlv_type_patch))
302+
return -EINVAL;
303+
297304
tlv = (struct tlv_type_hdr *)fw_data;
298305
type_len = le32_to_cpu(tlv->type_len);
299306
tlv_patch = (struct tlv_type_patch *)tlv->data;
@@ -333,6 +340,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
333340
break;
334341

335342
case TLV_TYPE_NVM:
343+
if (fw_size < sizeof(struct tlv_type_hdr))
344+
return -EINVAL;
345+
336346
tlv = (struct tlv_type_hdr *)fw_data;
337347

338348
type_len = le32_to_cpu(tlv->type_len);
@@ -341,17 +351,26 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
341351
BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
342352
BT_DBG("Length\t\t : %d bytes", length);
343353

354+
if (fw_size < length + (tlv->data - fw_data))
355+
return -EINVAL;
356+
344357
idx = 0;
345358
data = tlv->data;
346-
while (idx < length) {
359+
while (idx < length - sizeof(struct tlv_type_nvm)) {
347360
tlv_nvm = (struct tlv_type_nvm *)(data + idx);
348361

349362
tag_id = le16_to_cpu(tlv_nvm->tag_id);
350363
tag_len = le16_to_cpu(tlv_nvm->tag_len);
351364

365+
if (length < idx + sizeof(struct tlv_type_nvm) + tag_len)
366+
return -EINVAL;
367+
352368
/* Update NVM tags as needed */
353369
switch (tag_id) {
354370
case EDL_TAG_ID_HCI:
371+
if (tag_len < 3)
372+
return -EINVAL;
373+
355374
/* HCI transport layer parameters
356375
* enabling software inband sleep
357376
* onto controller side.
@@ -367,6 +386,9 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
367386
break;
368387

369388
case EDL_TAG_ID_DEEP_SLEEP:
389+
if (tag_len < 1)
390+
return -EINVAL;
391+
370392
/* Sleep enable mask
371393
* enabling deep sleep feature on controller.
372394
*/
@@ -375,14 +397,16 @@ static void qca_tlv_check_data(struct hci_dev *hdev,
375397
break;
376398
}
377399

378-
idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
400+
idx += sizeof(struct tlv_type_nvm) + tag_len;
379401
}
380402
break;
381403

382404
default:
383405
BT_ERR("Unknown TLV type %d", config->type);
384-
break;
406+
return -EINVAL;
385407
}
408+
409+
return 0;
386410
}
387411

388412
static int qca_tlv_send_segment(struct hci_dev *hdev, int seg_size,
@@ -532,7 +556,9 @@ static int qca_download_firmware(struct hci_dev *hdev,
532556
memcpy(data, fw->data, size);
533557
release_firmware(fw);
534558

535-
qca_tlv_check_data(hdev, config, data, soc_type);
559+
ret = qca_tlv_check_data(hdev, config, data, size, soc_type);
560+
if (ret)
561+
return ret;
536562

537563
segment = data;
538564
remain = size;

0 commit comments

Comments
 (0)