mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "David E. Box" <david.e.box@linux.intel.com>
To: "Hans de Goede" <hansg@kernel.org>,
	"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
	platform-driver-x86@vger.kernel.org
Cc: "David E. Box" <david.e.box@linux.intel.com>,
	linux-kernel@vger.kernel.org
Subject: [PATCH 4/6] platform/x86/intel/vsec: Validate discovery table bounds against the BAR
Date: Fri, 30 Oct 2026 18:29:36 -0700	[thread overview]
Message-ID: <20261031012939.377995-5-david.e.box@linux.intel.com> (raw)
In-Reply-To: <20261031012939.377995-1-david.e.box@linux.intel.com>

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@linux.intel.com>
---
 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


  parent reply	other threads:[~2026-10-31  1:29 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-31  1:29 [PATCH 0/6] platform/x86/intel: Harden PMT discovery against malformed device data David E. Box
2026-10-31  1:29 ` [PATCH 1/6] platform/x86/intel/pmt/discovery: Bound attr_size against feature_table capacity David E. Box
2026-10-31  1:29 ` [PATCH 2/6] platform/x86/intel/pmt/discovery: Avoid WARN for malformed tables David E. Box
2026-10-31  1:29 ` [PATCH 3/6] platform/x86/intel/pmt/discovery: Validate feature id before use David E. Box
2026-10-31  1:29 ` David E. Box [this message]
2026-10-31  1:29 ` [PATCH 5/6] platform/x86/intel/pmt/discovery: Validate feature table against the BAR David E. Box
2026-10-31  1:29 ` [PATCH 6/6] platform/x86/intel/pmt: Validate telemetry regions " David E. Box

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261031012939.377995-5-david.e.box@linux.intel.com \
    --to=david.e.box@linux.intel.com \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®