* [PATCH] ACPI: IORT: validate RMR node array bounds
@ 2026-07-06 9:43 Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
0 siblings, 1 reply; 2+ messages in thread
From: Pengpeng Hou @ 2026-07-06 9:43 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: Pengpeng Hou, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J. Wysocki, Len Brown, linux-acpi,
linux-arm-kernel, linux-kernel
IORT RMR nodes describe reserved-memory ranges through firmware
offset and count fields inside the current IORT node.
Validate the generic IORT node length before dispatching it, and
check both the RMR descriptor array and the ID mapping array before
walking them. This binds each array walk to the current node length
instead of only trusting the firmware-provided count.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index af7a9b2fd5bc..55373246bdd4 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -15,6 +15,7 @@
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -148,6 +149,69 @@ typedef acpi_status (*iort_find_node_callback)
/* Root pointer to the mapped IORT table */
static struct acpi_table_header *iort_table;
+static bool iort_node_valid(struct acpi_iort_node *node,
+ struct acpi_iort_node *end)
+{
+ size_t remaining;
+
+ if (WARN_TAINT(node >= end, TAINT_FIRMWARE_WORKAROUND,
+ "IORT node pointer overflows, bad table!\n"))
+ return false;
+
+ remaining = (char *)end - (char *)node;
+ if (WARN_TAINT(remaining < sizeof(*node), TAINT_FIRMWARE_WORKAROUND,
+ "IORT node header is truncated, bad table!\n"))
+ return false;
+
+ if (WARN_TAINT(node->length < sizeof(*node) || node->length > remaining,
+ TAINT_FIRMWARE_WORKAROUND,
+ "IORT node length overflows, bad table!\n"))
+ return false;
+
+ return true;
+}
+
+static bool iort_node_array_valid(struct acpi_iort_node *node, u32 offset,
+ u32 count, size_t elem_size,
+ size_t min_offset, const char *name)
+{
+ size_t bytes;
+
+ if (!count)
+ return true;
+
+ if (!offset || offset < min_offset || offset > node->length) {
+ pr_err(FW_BUG "Invalid %s offset in IORT node %p\n", name,
+ node);
+ return false;
+ }
+
+ if (check_mul_overflow(count, elem_size, &bytes) ||
+ bytes > node->length - offset) {
+ pr_err(FW_BUG "Invalid %s array in IORT node %p\n", name,
+ node);
+ return false;
+ }
+
+ return true;
+}
+
+static bool iort_rmr_node_valid(struct acpi_iort_node *node)
+{
+ struct acpi_iort_rmr *rmr;
+
+ if (node->length < sizeof(*node) + sizeof(*rmr)) {
+ pr_err(FW_BUG "Truncated RMR node in IORT table\n");
+ return false;
+ }
+
+ rmr = (struct acpi_iort_rmr *)node->node_data;
+ return iort_node_array_valid(node, rmr->rmr_offset, rmr->rmr_count,
+ sizeof(struct acpi_iort_rmr_desc),
+ sizeof(*node) + sizeof(*rmr),
+ "RMR descriptor");
+}
+
static LIST_HEAD(iort_msi_chip_list);
static DEFINE_SPINLOCK(iort_msi_chip_lock);
@@ -243,8 +307,7 @@ static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
iort_table->length);
for (i = 0; i < iort->node_count; i++) {
- if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
- "IORT node pointer overflows, bad table!\n"))
+ if (!iort_node_valid(iort_node, iort_end))
return NULL;
if (iort_node->type == type &&
@@ -1014,6 +1077,9 @@ static void iort_get_rmrs(struct acpi_iort_node *node,
struct acpi_iort_rmr_desc *rmr_desc;
int i;
+ if (!iort_rmr_node_valid(node))
+ return;
+
rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, node,
rmr->rmr_offset);
@@ -1114,6 +1180,16 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
return;
}
+ if (!iort_node_array_valid(node, node->mapping_offset,
+ node->mapping_count,
+ sizeof(struct acpi_iort_id_mapping),
+ sizeof(*node),
+ "ID mapping"))
+ return;
+
+ if (!iort_rmr_node_valid(node))
+ return;
+
rmr = (struct acpi_iort_rmr *)node->node_data;
if (!rmr->rmr_offset || !rmr->rmr_count)
return;
@@ -1175,8 +1251,7 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
iort_table->length);
for (i = 0; i < iort->node_count; i++) {
- if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
- "IORT node pointer overflows, bad table!\n"))
+ if (!iort_node_valid(iort_node, iort_end))
return;
if (iort_node->type == ACPI_IORT_NODE_RMR)
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH] ACPI: IORT: validate RMR node array bounds
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
@ 2026-07-31 15:42 ` Will Deacon
0 siblings, 0 replies; 2+ messages in thread
From: Will Deacon @ 2026-07-31 15:42 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Rafael J. Wysocki, Len Brown, linux-acpi, linux-arm-kernel,
linux-kernel
On Mon, Jul 06, 2026 at 05:43:00PM +0800, Pengpeng Hou wrote:
> IORT RMR nodes describe reserved-memory ranges through firmware
> offset and count fields inside the current IORT node.
>
> Validate the generic IORT node length before dispatching it, and
> check both the RMR descriptor array and the ID mapping array before
> walking them. This binds each array walk to the current node length
> instead of only trusting the firmware-provided count.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 79 insertions(+), 4 deletions(-)
Hanjun, Lorenzo, any thoughts on this one?
Will
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-31 15:42 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).