}
 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;
 
        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;
 
                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;
                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);
                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.
                                break;
 
                        case EDL_TAG_ID_DEEP_SLEEP:
+                               if (tag_len < 1)
+                                       return -EINVAL;
+
                                /* Sleep enable mask
                                 * enabling deep sleep feature on controller.
                                 */
                                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,
        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;