[PATCH v1] Bluetooth: btintel_pcie: cap device debug regions at 1 MB
From: Kiran K <hidden>
Date: 2026-09-08 13:14:19
Subsystem:
bluetooth drivers, the rest · Maintainers:
Marcel Holtmann, Luiz Augusto von Dentz, Linus Torvalds
Validate device-supplied debug-region boundaries during mailbox TLV
parsing and convert start/end values to a validated region size. Cap
debug-region sizes to 1 MB to prevent out-of-bounds reads if firmware
reports oversized memory ranges. Skip invalid or oversized regions
prior to coredump capture.
This issue was reported by Claude Mythos.
Fixes: 755cf7adf8dd ("Bluetooth: btintel_pcie: unified decoder coredump format")
Assisted-by: Copilot:claude-opus-4.7
Signed-off-by: Kiran K <redacted>
---
drivers/bluetooth/btintel_pcie.c | 229 ++++++++++++++++---------------
drivers/bluetooth/btintel_pcie.h | 16 +--
2 files changed, 128 insertions(+), 117 deletions(-)
diff --git a/drivers/bluetooth/btintel_pcie.c b/drivers/bluetooth/btintel_pcie.c
index 04f5b0273977..652777d1026f 100644
--- a/drivers/bluetooth/btintel_pcie.c
+++ b/drivers/bluetooth/btintel_pcie.c@@ -960,13 +960,6 @@ static int btintel_pcie_dump_target_region(struct btintel_pcie_data *data, return 1; } - if (size > BTINTEL_PCIE_REGION_MAX_SIZE) { - bt_dev_warn(data->hdev, - "Skipping dump region %s: size %u exceeds max %u", - name, size, BTINTEL_PCIE_REGION_MAX_SIZE); - return 1; - } - if (addr_start > U32_MAX - size) { bt_dev_warn(data->hdev, "Skipping dump region %s: addr_start 0x%08x + size %u would overflow",
@@ -1196,28 +1189,10 @@ static bool btintel_pcie_is_mdbgc_supported(struct btintel_pcie_data *data) static void btintel_pcie_dump_mem_range(struct btintel_pcie_data *data, struct list_head *list, u32 region_id, const char *name, u32 addr_start, - u32 addr_end, u64 *regions_mask) + u32 region_size, u64 *regions_mask) { - u32 region_size; - - if (!addr_start || !addr_end || addr_end < addr_start) - return; - - if (addr_end > U32_MAX - 0x04) { - bt_dev_warn(data->hdev, - "Skipping dump region %s: addr_end 0x%08x would overflow", - name, addr_end); - return; - } - - region_size = (addr_end + 0x04) - addr_start; - if (region_size > BTINTEL_PCIE_REGION_MAX_SIZE) { - bt_dev_warn(data->hdev, - "Skipping dump region %s: size %u exceeds max %u", - name, region_size, - BTINTEL_PCIE_REGION_MAX_SIZE); + if (!region_size) return; - } if (!btintel_pcie_dump_target_region(data, list, region_id, name, addr_start, region_size))
@@ -1228,16 +1203,14 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data) { struct btintel_pcie_dbgc *dbgc = NULL; struct btintel_pcie_mdbgc *mdbgc = NULL; + struct btintel_pcie_dump_mem_info *dump_info = &data->dump_info; struct hci_dev *hdev = data->hdev; struct btintel_pcie_dump_entry *entry; struct btintel_pcie_dump_file_hdr *file_hdr; struct scatterlist *sg_dump_data; u32 status_reg, wrap_reg; - u32 exception_dump_len; - u32 exc_addr; u64 regions_mask = 0; u8 hw_variant; - u32 smem_rd_addr = 0, smem_rd_size = 0; u32 file_len; u8 count; int ret;
@@ -1270,35 +1243,6 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data) return -EINVAL; } - smem_rd_addr = data->dump_info.smem_addr_start; - smem_rd_size = 0; - - if (!smem_rd_addr && !data->dump_info.smem_addr_end) { - bt_dev_dbg(hdev, "smem region not advertised by firmware"); - } else if (data->dump_info.smem_addr_end < smem_rd_addr || - data->dump_info.smem_addr_end > U32_MAX - 0x04) { - bt_dev_err(hdev, - "Invalid smem region: start=0x%08x end=0x%08x", - smem_rd_addr, data->dump_info.smem_addr_end); - } else { - smem_rd_size = (data->dump_info.smem_addr_end + 0x04) - - smem_rd_addr; - - bt_dev_dbg(hdev, - "smem_region: smem_start_addr=0x%08x smem_end_addr=0x%08x smem_rd_size=%u", - smem_rd_addr, data->dump_info.smem_addr_end, - smem_rd_size); - - if (smem_rd_size == 0 || - smem_rd_size > BTINTEL_PCIE_SMEM_MAX_SIZE) { - bt_dev_err(hdev, - "Invalid smem region: smem_rd_addr 0x%08x size %u (max %u)", - smem_rd_addr, smem_rd_size, - BTINTEL_PCIE_SMEM_MAX_SIZE); - smem_rd_size = 0; - } - } - if (btintel_pcie_is_mdbgc_supported(data)) { ret = btintel_pcie_dump_dram_monitor(data, &dump_list, count, mdbgc->buf1, status_reg,
@@ -1350,47 +1294,46 @@ static int btintel_pcie_read_debug_regions(struct btintel_pcie_data *data) "Failed to dump DRAM region: %d", ret); } - if (smem_rd_size && + if (dump_info->smem_size && !btintel_pcie_dump_smem_monitor_region(data, &dump_list, BTINTEL_PCIE_INI_ID_SMEM, "monitor_smem", - smem_rd_addr, - smem_rd_size)) + dump_info->smem_addr_start, + dump_info->smem_size)) regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_SMEM); - exc_addr = data->dump_info.exception_dump_addr; - exception_dump_len = data->dump_info.exception_dump_len; - if (exc_addr && exception_dump_len) { + if (dump_info->exception_dump_addr && dump_info->exception_dump_len) { ret = btintel_pcie_dump_target_region(data, &dump_list, BTINTEL_PCIE_INI_ID_EXCEPTION_EVT, - "EXCEPTION_EVT_BUFFER", exc_addr, - exception_dump_len); + "EXCEPTION_EVT_BUFFER", + dump_info->exception_dump_addr, + dump_info->exception_dump_len); if (!ret) regions_mask |= BIT_ULL(BTINTEL_PCIE_INI_ID_EXCEPTION_EVT); } btintel_pcie_dump_mem_range(data, &dump_list, BTINTEL_PCIE_INI_ID_DCCM, "DCCM", - data->dump_info.dccm_addr_start, - data->dump_info.dccm_addr_end, + dump_info->dccm_addr_start, + dump_info->dccm_size, ®ions_mask); btintel_pcie_dump_mem_range(data, &dump_list, BTINTEL_PCIE_INI_ID_SDS, "SDS", - data->dump_info.sds_start_addr_start, - data->dump_info.sds_start_addr_end, + dump_info->sds_start_addr_start, + dump_info->sds_start_size, ®ions_mask); btintel_pcie_dump_mem_range(data, &dump_list, BTINTEL_PCIE_INI_ID_SDS_IOSF, "SDS_IOSF", - data->dump_info.sds_iosf_data_addr_start, - data->dump_info.sds_iosf_data_addr_end, + dump_info->sds_iosf_data_addr_start, + dump_info->sds_iosf_data_size, ®ions_mask); btintel_pcie_dump_mem_range(data, &dump_list, BTINTEL_PCIE_INI_ID_ECL, "ECL_REGION", - data->dump_info.ecl_addr_start, - data->dump_info.ecl_addr_end, + dump_info->ecl_addr_start, + dump_info->ecl_size, ®ions_mask); ret = btintel_pcie_dump_info(data, &dump_list, regions_mask);
@@ -1593,6 +1536,34 @@ static inline bool btintel_pcie_in_error(struct btintel_pcie_data *data) return data->boot_stage_cache & BTINTEL_PCIE_CSR_BOOT_STAGE_ABORT_HANDLER; } +static u32 btintel_pcie_region_size(struct hci_dev *hdev, const char *name, + u32 start, u32 end) +{ + u64 size; + + if (!start || !end) + return 0; + + if (end < start) { + bt_dev_warn(hdev, "%s region invalid: start=0x%08x end=0x%08x", + name, start, end); + return 0; + } + + size = (u64)end + 0x04 - start; + if (size % sizeof(u32)) + bt_dev_warn(hdev, "%s region size %llu is not 4-byte aligned", + name, size); + + if (size > BTINTEL_PCIE_REGION_MAX_SIZE) { + bt_dev_warn(hdev, "%s region size %llu exceeds cap %u, ignoring", + name, size, BTINTEL_PCIE_REGION_MAX_SIZE); + return 0; + } + + return (u32)size; +} + static const char *btintel_pcie_tlv_str(u8 tlv_type) { switch (tlv_type) {
@@ -1720,6 +1691,15 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) get_unaligned_le32(&tlv->val[0]); data->dump_info.exception_dump_len = get_unaligned_le32(&tlv->val[4]); + if (data->dump_info.exception_dump_len > + BTINTEL_PCIE_REGION_MAX_SIZE) { + bt_dev_warn(data->hdev, + "EXCEPTION_EVT_BUFFER size %u exceeds cap %u, ignoring", + data->dump_info.exception_dump_len, + BTINTEL_PCIE_REGION_MAX_SIZE); + data->dump_info.exception_dump_addr = 0; + data->dump_info.exception_dump_len = 0; + } break; case BTINTEL_PCIE_TLV_TYPE_DCCM_MEM_ADDRESS: if (tlv_len < 8) {
@@ -1731,10 +1711,16 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) } data->dump_info.dccm_addr_start = get_unaligned_le32(&tlv->val[0]); - data->dump_info.dccm_addr_end = - get_unaligned_le32(&tlv->val[4]); + data->dump_info.dccm_size = + btintel_pcie_region_size(data->hdev, "DCCM", + data->dump_info.dccm_addr_start, + get_unaligned_le32(&tlv->val[4])); + if (!data->dump_info.dccm_size) + data->dump_info.dccm_addr_start = 0; break; - case BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS: + case BTINTEL_PCIE_TLV_TYPE_SDS_MEM_ADDRESS: { + struct btintel_pcie_dump_mem_info *di = &data->dump_info; + /* hw_variant comes from cnvi_bt which is set during * setup. If mailbox fires before setup completes, * hw_variant is 0. Skip SDS parsing in that case.
@@ -1744,35 +1730,52 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) break; } if (tlv_len == 16 && hw_variant > BTINTEL_HWID_BZRI) { - data->dump_info.sds_start_addr_start = + di->sds_start_addr_start = get_unaligned_le32(&tlv->val[0]); - data->dump_info.sds_start_addr_end = - get_unaligned_le32(&tlv->val[4]); - data->dump_info.sds_iosf_data_addr_start = + di->sds_start_size = + btintel_pcie_region_size(data->hdev, "SDS", + di->sds_start_addr_start, + get_unaligned_le32(&tlv->val[4])); + di->sds_iosf_data_addr_start = get_unaligned_le32(&tlv->val[8]); - data->dump_info.sds_iosf_data_addr_end = - get_unaligned_le32(&tlv->val[12]); + di->sds_iosf_data_size = + btintel_pcie_region_size(data->hdev, "SDS_IOSF", + di->sds_iosf_data_addr_start, + get_unaligned_le32(&tlv->val[12])); } else if (tlv_len == 24 && (hw_variant == BTINTEL_HWID_BZRI || hw_variant == BTINTEL_HWID_BZRIW)) { - data->dump_info.sds_fixed_rom_addr_start = + di->sds_fixed_rom_addr_start = get_unaligned_le32(&tlv->val[0]); - data->dump_info.sds_fixed_rom_addr_end = - get_unaligned_le32(&tlv->val[4]); - data->dump_info.sds_start_addr_start = + di->sds_fixed_rom_size = + btintel_pcie_region_size(data->hdev, "SDS_FIXED_ROM", + di->sds_fixed_rom_addr_start, + get_unaligned_le32(&tlv->val[4])); + di->sds_start_addr_start = get_unaligned_le32(&tlv->val[8]); - data->dump_info.sds_start_addr_end = - get_unaligned_le32(&tlv->val[12]); - data->dump_info.sds_iosf_data_addr_start = + di->sds_start_size = + btintel_pcie_region_size(data->hdev, "SDS", + di->sds_start_addr_start, + get_unaligned_le32(&tlv->val[12])); + di->sds_iosf_data_addr_start = get_unaligned_le32(&tlv->val[16]); - data->dump_info.sds_iosf_data_addr_end = - get_unaligned_le32(&tlv->val[20]); + di->sds_iosf_data_size = + btintel_pcie_region_size(data->hdev, "SDS_IOSF", + di->sds_iosf_data_addr_start, + get_unaligned_le32(&tlv->val[20])); } else { bt_dev_err(data->hdev, "SDS TLV: hw=0x%2.2x len=%u", hw_variant, tlv_len); } + if (!di->sds_start_size) + di->sds_start_addr_start = 0; + if (!di->sds_iosf_data_size) + di->sds_iosf_data_addr_start = 0; + if (!di->sds_fixed_rom_size) + di->sds_fixed_rom_addr_start = 0; break; + } case BTINTEL_PCIE_TLV_TYPE_ECL_MEM_ADDRESS: if (tlv_len < 8) { bt_dev_err(data->hdev,
@@ -1783,8 +1786,12 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) } data->dump_info.ecl_addr_start = get_unaligned_le32(&tlv->val[0]); - data->dump_info.ecl_addr_end = - get_unaligned_le32(&tlv->val[4]); + data->dump_info.ecl_size = + btintel_pcie_region_size(data->hdev, "ECL", + data->dump_info.ecl_addr_start, + get_unaligned_le32(&tlv->val[4])); + if (!data->dump_info.ecl_size) + data->dump_info.ecl_addr_start = 0; break; case BTINTEL_PCIE_TLV_TYPE_SMEM_ADDRESS: if (tlv_len < 8) {
@@ -1796,8 +1803,12 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) } data->dump_info.smem_addr_start = get_unaligned_le32(&tlv->val[0]); - data->dump_info.smem_addr_end = - get_unaligned_le32(&tlv->val[4]); + data->dump_info.smem_size = + btintel_pcie_region_size(data->hdev, "SMEM", + data->dump_info.smem_addr_start, + get_unaligned_le32(&tlv->val[4])); + if (!data->dump_info.smem_size) + data->dump_info.smem_addr_start = 0; break; default: bt_dev_dbg(data->hdev, "Unknown TLV type: %u length: %u",
@@ -1815,29 +1826,29 @@ static int btintel_parse_mbox_tlv(struct btintel_pcie_data *data) data->dump_info.exception_dump_addr, data->dump_info.exception_dump_len); bt_dev_info(data->hdev, - "dccm: start:0x%08x end:0x%08x", + "dccm: start:0x%8.8x size:%u", data->dump_info.dccm_addr_start, - data->dump_info.dccm_addr_end); + data->dump_info.dccm_size); bt_dev_info(data->hdev, - "sds_fixed_rom: start:0x%08x end:0x%08x", + "sds_fixed_rom: start:0x%8.8x size:%u", data->dump_info.sds_fixed_rom_addr_start, - data->dump_info.sds_fixed_rom_addr_end); + data->dump_info.sds_fixed_rom_size); bt_dev_info(data->hdev, - "sds: start:0x%08x end:0x%08x", + "sds: start:0x%8.8x size:%u", data->dump_info.sds_start_addr_start, - data->dump_info.sds_start_addr_end); + data->dump_info.sds_start_size); bt_dev_info(data->hdev, - "sds_iosf: start:0x%08x end:0x%08x", + "sds_iosf: start:0x%8.8x size:%u", data->dump_info.sds_iosf_data_addr_start, - data->dump_info.sds_iosf_data_addr_end); + data->dump_info.sds_iosf_data_size); bt_dev_info(data->hdev, - "ecl: start:0x%08x end:0x%08x", + "ecl: start:0x%8.8x size:%u", data->dump_info.ecl_addr_start, - data->dump_info.ecl_addr_end); + data->dump_info.ecl_size); bt_dev_info(data->hdev, - "smem: start:0x%08x end:0x%08x", + "smem: start:0x%8.8x size:%u", data->dump_info.smem_addr_start, - data->dump_info.smem_addr_end); + data->dump_info.smem_size); vfree(buffer); return 0;
diff --git a/drivers/bluetooth/btintel_pcie.h b/drivers/bluetooth/btintel_pcie.h
index 9d84db48a8e5..f35f80f800ed 100644
--- a/drivers/bluetooth/btintel_pcie.h
+++ b/drivers/bluetooth/btintel_pcie.h@@ -76,8 +76,8 @@ #define BTINTEL_PCIE_DBGC_DBGBUFF_WRAP_ARND_SCP (BTINTEL_PCIE_DBGC_BASE_ADDR_SCP + 0x2C) #define BTINTEL_PCIE_DBGC_ALLOCATION_OFFSET 0x100 -#define BTINTEL_PCIE_SMEM_MAX_SIZE (16 * 1024) -#define BTINTEL_PCIE_REGION_MAX_SIZE (16 * 1024 * 1024) +/* Upper bound for device-supplied debug-region sizes. */ +#define BTINTEL_PCIE_REGION_MAX_SIZE (1 * 1024 * 1024) #define BTINTEL_PCIE_DBG_IDX_BIT_MASK 0x0F #define BTINTEL_PCIE_DBGC_DBG_BUF_IDX(data) (((data) >> 24) & BTINTEL_PCIE_DBG_IDX_BIT_MASK) #define BTINTEL_PCIE_DBG_OFFSET_BIT_MASK 0xFFFFFF
@@ -497,17 +497,17 @@ struct btintel_pcie_dump_mem_info { u32 exception_dump_addr; u32 exception_dump_len; u32 dccm_addr_start; - u32 dccm_addr_end; + u32 dccm_size; u32 sds_fixed_rom_addr_start; - u32 sds_fixed_rom_addr_end; + u32 sds_fixed_rom_size; u32 sds_start_addr_start; - u32 sds_start_addr_end; + u32 sds_start_size; u32 sds_iosf_data_addr_start; - u32 sds_iosf_data_addr_end; + u32 sds_iosf_data_size; u32 ecl_addr_start; - u32 ecl_addr_end; + u32 ecl_size; u32 smem_addr_start; - u32 smem_addr_end; + u32 smem_size; }; struct btintel_pcie_mbox {
--
2.54.0