[PATCH v2 1/3] ACPI: RIMT: validate table and node extents before traversal

Pengpeng Hou <[email protected]>
Newsgroups gmane.linux.ports.riscv,gmane.linux.acpi.devel,gmane.linux.kernel
Message-ID <[email protected]>
RIMT walkers construct the first node from a firmware-provided offset and
advance by each node length.  Checking only whether the current pointer
reached the table end still permits a bad root offset, a truncated node
header, or a zero or oversized node length to be read.

Validate the root node area before constructing the first pointer.  Bound
traversal by num_nodes and require every complete node to fit in the table.

Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <[email protected]>
---
 drivers/acpi/riscv/rimt.c | 53 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 50 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c
index e4538fa6c2c8..533e9aba6e97 100644
--- a/drivers/acpi/riscv/rimt.c
+++ b/drivers/acpi/riscv/rimt.c
@@ -31,6 +31,45 @@ static DEFINE_SPINLOCK(rimt_fwnode_lock);
 /* Root pointer to the mapped RIMT table */
 static struct acpi_table_header *rimt_table;
 
+static bool rimt_table_valid(void)
+{
+	struct acpi_table_rimt *rimt = (struct acpi_table_rimt *)rimt_table;
+	size_t node_bytes;
+
+	if (!rimt || rimt->header.length < sizeof(*rimt))
+		return false;
+
+	if (!rimt->num_nodes)
+		return true;
+
+	if (rimt->node_offset < sizeof(*rimt) ||
+	    rimt->node_offset > rimt->header.length -
+				 sizeof(struct acpi_rimt_node))
+		return false;
+
+	node_bytes = rimt->header.length - rimt->node_offset;
+	return rimt->num_nodes <=
+		node_bytes / sizeof(struct acpi_rimt_node);
+}
+
+static bool rimt_node_valid(struct acpi_rimt_node *node,
+			    struct acpi_rimt_node *end)
+{
+	size_t remaining;
+
+	if ((u8 *)node >= (u8 *)end)
+		return false;
+
+	remaining = (u8 *)end - (u8 *)node;
+	if (remaining < sizeof(*node) || node->length < sizeof(*node) ||
+	    node->length > remaining) {
+		pr_err(FW_BUG "Invalid RIMT node length\n");
+		return false;
+	}
+
+	return true;
+}
+
 /**
  * rimt_set_fwnode() - Create rimt_fwnode and use it to register
  *		       iommu data in the rimt_fwnode_list
@@ -153,19 +192,21 @@ static struct acpi_rimt_node *rimt_scan_node(enum acpi_rimt_node_type type,
 	struct acpi_table_rimt *rimt;
 	int i;
 
-	if (!rimt_table)
+	if (!rimt_table_valid())
 		return NULL;
 
 	/* Get the first RIMT node */
 	rimt = (struct acpi_table_rimt *)rimt_table;
+	if (!rimt->num_nodes)
+		return NULL;
+
 	rimt_node = ACPI_ADD_PTR(struct acpi_rimt_node, rimt,
 				 rimt->node_offset);
 	rimt_end = ACPI_ADD_PTR(struct acpi_rimt_node, rimt_table,
 				rimt_table->length);
 
 	for (i = 0; i < rimt->num_nodes; i++) {
-		if (WARN_TAINT(rimt_node >= rimt_end, TAINT_FIRMWARE_WORKAROUND,
-			       "RIMT node pointer overflows, bad table!\n"))
+		if (!rimt_node_valid(rimt_node, rimt_end))
 			return NULL;
 
 		if (rimt_node->type == type &&
@@ -525,4 +566,10 @@ void __init riscv_acpi_rimt_init(void)
 
 		return;
 	}
+
+	if (!rimt_table_valid()) {
+		pr_err(FW_BUG "Invalid RIMT table layout\n");
+		acpi_put_table(rimt_table);
+		rimt_table = NULL;
+	}
 }
-- 
2.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.