[PATCH 4/6] platform/x86/intel/vsec: Validate discovery table bounds against the BAR
From: David E. Box
Date: Fri Oct 30 2026 - 21:30:05 EST
The DVSEC/VSEC table offset, entry size, and entry count are read from
device configuration space, but the resulting resource ranges were never
checked against the BAR they claim to reside in. A device reporting a
large offset could produce resources outside its BAR, pointing at unmapped
physical address gaps or MMIO belonging to other devices.
Additionally, INTEL_DVSEC_TABLE_BAR() masks the table register to three
bits, so tbir may be 6 or 7 even though only BARs 0-5 are valid. Those
values silently selected the ROM BAR or a bridge resource slot.
Reject headers whose tbir (bar index) is out of range or whose BAR is not a
populated memory BAR, and any discovery table resource that does not fit
within that BAR. These rejections cause the capability to not be
registered, so log them as warnings.
The PMT drivers also map ranges built from discovery data, but they only
receive the resolved discovery table resources and cannot tell which BAR
those came from. Record the discovery BAR index in struct
intel_vsec_device, along with whether the tables are BAR relative at all,
and export intel_vsec_check_bar_resource() to check a resource against a
given BAR.
Fixes: a3c8f906ed5f ("platform/x86/intel: Move intel_pmt from MFD to Auxiliary Bus")
Assisted-by: LLM
Signed-off-by: David E. Box <david.e.box@xxxxxxxxxxxxxxx>
---
drivers/platform/x86/intel/vsec.c | 93 +++++++++++++++++++++++++++++--
include/linux/intel_vsec.h | 8 +++
2 files changed, 96 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c
index 380d8231549f..4395663d8edd 100644
--- a/drivers/platform/x86/intel/vsec.c
+++ b/drivers/platform/x86/intel/vsec.c
@@ -104,6 +104,76 @@ static bool intel_vsec_supported(u16 id, unsigned long caps)
}
}
+/* Return the resource of a populated memory BAR, or NULL if there is none */
+static const struct resource *intel_vsec_mem_bar(struct pci_dev *pdev,
+ unsigned int bar)
+{
+ if (bar >= PCI_STD_NUM_BARS)
+ return NULL;
+
+ if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
+ return NULL;
+
+ return pci_resource_n(pdev, bar);
+}
+
+/**
+ * intel_vsec_check_bar_resource() - Check a resource lies within a PCI BAR
+ * @intel_vsec_dev: vsec device the resource was derived from
+ * @bar: index of the BAR the device places @res in
+ * @res: resource to check
+ *
+ * Discovery table locations and the tables they reference are read from the
+ * device, so ranges built from them must be checked against the BAR they
+ * claim to live in before being mapped. For a resource in the same BAR as
+ * the discovery tables, pass @intel_vsec_dev->tbir as @bar.
+ *
+ * Return: 0 if @res is within BAR @bar or the device's tables are not BAR
+ * relative, -EINVAL if @bar is not a valid memory BAR or @res is outside it.
+ */
+int intel_vsec_check_bar_resource(struct intel_vsec_device *intel_vsec_dev,
+ unsigned int bar, const struct resource *res)
+{
+ const struct resource *bar_res;
+
+ /* ACPI tables are read from _DSD, and a supplied base is not BAR relative */
+ if (!intel_vsec_dev->bar_relative)
+ return 0;
+
+ bar_res = intel_vsec_mem_bar(to_pci_dev(intel_vsec_dev->dev), bar);
+ if (!bar_res || !resource_contains(bar_res, res))
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(intel_vsec_check_bar_resource, "INTEL_VSEC");
+
+/*
+ * For DVSEC/VSEC discovery the table location is read from device config
+ * space. Check that it names a memory BAR the tables can be checked against.
+ */
+static int intel_vsec_check_table_bar(struct device *dev,
+ const struct intel_vsec_header *header,
+ const struct intel_vsec_platform_info *info,
+ bool *bar_relative)
+{
+ *bar_relative = false;
+
+ /* Not BAR relative: ACPI tables come from _DSD, or a base was supplied */
+ if (info->src == INTEL_VSEC_DISC_ACPI || info->base_addr || !dev_is_pci(dev))
+ return 0;
+
+ if (!intel_vsec_mem_bar(to_pci_dev(dev), header->tbir)) {
+ dev_warn(dev, "BAR %u is not a valid memory BAR for header id %d\n",
+ header->tbir, header->id);
+ return -EINVAL;
+ }
+
+ *bar_relative = true;
+
+ return 0;
+}
+
static void intel_vsec_remove_aux(void *data)
{
auxiliary_device_delete(data);
@@ -297,7 +367,8 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
struct resource *tmp;
struct device *parent;
unsigned long quirks = info->quirks;
- int i;
+ bool bar_relative;
+ int i, ret;
if (info->parent)
parent = info->parent;
@@ -317,16 +388,23 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
return -EINVAL;
}
+ if (quirks & VSEC_QUIRK_TABLE_SHIFT)
+ header->offset >>= TABLE_OFFSET_SHIFT;
+
+ ret = intel_vsec_check_table_bar(dev, header, info, &bar_relative);
+ if (ret)
+ return ret;
+
intel_vsec_dev = kzalloc_flex(*intel_vsec_dev, resource, header->num_entries);
if (!intel_vsec_dev)
return -ENOMEM;
intel_vsec_dev->num_resources = header->num_entries;
+ intel_vsec_dev->dev = dev;
+ intel_vsec_dev->tbir = header->tbir;
+ intel_vsec_dev->bar_relative = bar_relative;
res = intel_vsec_dev->resource;
- if (quirks & VSEC_QUIRK_TABLE_SHIFT)
- header->offset >>= TABLE_OFFSET_SHIFT;
-
/*
* The DVSEC/VSEC contains the starting offset and count for a block of
* discovery tables. Create a resource array of these tables to the
@@ -344,6 +422,12 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1;
tmp->flags = IORESOURCE_MEM;
+ if (intel_vsec_check_bar_resource(intel_vsec_dev, header->tbir, tmp)) {
+ dev_warn(dev, "Discovery table %d for header id %d exceeds BAR %u\n",
+ i, header->id, header->tbir);
+ return -EINVAL;
+ }
+
/* Check resource is not in use */
if (!request_mem_region(tmp->start, resource_size(tmp), ""))
return -EBUSY;
@@ -351,7 +435,6 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head
release_mem_region(tmp->start, resource_size(tmp));
}
- intel_vsec_dev->dev = dev;
intel_vsec_dev->quirks = info->quirks;
intel_vsec_dev->base_addr = info->base_addr;
intel_vsec_dev->priv_data = info->priv_data;
diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h
index 8686e79c4f75..feb4420617bc 100644
--- a/include/linux/intel_vsec.h
+++ b/include/linux/intel_vsec.h
@@ -160,6 +160,10 @@ struct intel_vsec_platform_info {
* @quirks: specified quirks
* @base_addr: base address of entries (if specified)
* @cap_id: the enumerated id of the vsec feature
+ * @tbir: PCI BAR index of the discovery tables. Valid only when
+ * @bar_relative is set
+ * @bar_relative: discovery tables are located in PCI BAR @tbir. Not set for
+ * ACPI discovery or a supplied base address
* @resource: PCI discovery resources (BAR windows), one per discovery
* instance. Valid only when @src == INTEL_VSEC_DISC_PCI
*/
@@ -176,6 +180,8 @@ struct intel_vsec_device {
unsigned long quirks;
u64 base_addr;
unsigned long cap_id;
+ u8 tbir;
+ bool bar_relative;
struct resource resource[] __counted_by(num_resources);
};
@@ -219,6 +225,8 @@ struct pmt_feature_group {
int intel_vsec_add_aux(struct device *parent,
struct intel_vsec_device *intel_vsec_dev,
const char *name);
+int intel_vsec_check_bar_resource(struct intel_vsec_device *intel_vsec_dev,
+ unsigned int bar, const struct resource *res);
static inline struct intel_vsec_device *dev_to_ivdev(struct device *dev)
{
--
2.43.0