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

Pengpeng Hou <[email protected]>
Newsgroups org.kernel.vger.linux-acpi,org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
RHCT consumers construct the first node from firmware node_offset and
advance using each node length.  A bad offset, truncated header, or zero or
oversized length can make traversal leave the table or fail to progress.

Validate the root node area before constructing a pointer.  Iterate at most
node_count entries and require every complete node to remain within the
table.

Fixes: e6b9d8eddb17 ("drivers/acpi: RISC-V: Add RHCT related code")
Fixes: 9ca87564190c ("RISC-V: ACPI: RHCT: Add function to get CBO block sizes")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <[email protected]>
---
 drivers/acpi/riscv/rhct.c | 100 ++++++++++++++++++++++++++++++--------
 1 file changed, 81 insertions(+), 19 deletions(-)

diff --git a/drivers/acpi/riscv/rhct.c b/drivers/acpi/riscv/rhct.c
index 8f3f38c64a88..01d6e39c0c5a 100644
--- a/drivers/acpi/riscv/rhct.c
+++ b/drivers/acpi/riscv/rhct.c
@@ -10,6 +10,44 @@
 #include <linux/acpi.h>
 #include <linux/bits.h>
 
+static bool rhct_table_valid(struct acpi_table_rhct *rhct)
+{
+	size_t node_bytes;
+
+	if (!rhct || rhct->header.length < sizeof(*rhct))
+		return false;
+
+	if (!rhct->node_count)
+		return true;
+
+	if (rhct->node_offset < sizeof(*rhct) ||
+	    rhct->node_offset > rhct->header.length -
+				 sizeof(struct acpi_rhct_node_header))
+		return false;
+
+	node_bytes = rhct->header.length - rhct->node_offset;
+	return rhct->node_count <=
+		node_bytes / sizeof(struct acpi_rhct_node_header);
+}
+
+static bool rhct_node_valid(struct acpi_rhct_node_header *node,
+			    struct acpi_rhct_node_header *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 RHCT node length\n");
+		return false;
+	}
+
+	return true;
+}
+
 static struct acpi_table_rhct *acpi_get_rhct(void)
 {
 	static struct acpi_table_header *rhct;
@@ -45,6 +83,7 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const
 	struct acpi_table_rhct *rhct;
 	u32 *hart_info_node_offset;
 	u32 acpi_cpu_id;
+	unsigned int i;
 	int ret;
 
 	BUG_ON(acpi_disabled);
@@ -61,28 +100,39 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const
 		rhct = (struct acpi_table_rhct *)table;
 	}
 
-	end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length);
+	if (!rhct_table_valid(rhct))
+		return -EINVAL;
+	if (!rhct->node_count)
+		return -ENOENT;
+
+	node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct,
+			    rhct->node_offset);
+	end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct,
+			   rhct->header.length);
+
+	for (i = 0; i < rhct->node_count; i++) {
+		if (!rhct_node_valid(node, end))
+			return -EINVAL;
 
-	for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset);
-	     node < end;
-	     node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) {
 		if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) {
 			hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr);
 			hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo);
-			if (acpi_cpu_id != hart_info->uid)
-				continue;
-
-			for (int i = 0; i < hart_info->num_offsets; i++) {
-				ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header,
-							rhct, hart_info_node_offset[i]);
-				if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) {
-					isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string,
-								ref_node, size_hdr);
-					*isa = isa_node->isa;
-					return 0;
+			if (acpi_cpu_id == hart_info->uid) {
+				for (int j = 0; j < hart_info->num_offsets; j++) {
+					ref_node = ACPI_ADD_PTR(struct acpi_rhct_node_header,
+								rhct, hart_info_node_offset[j]);
+					if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) {
+						isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string,
+									ref_node, size_hdr);
+						*isa = isa_node->isa;
+						return 0;
+					}
 				}
 			}
 		}
+
+		node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node,
+				    node->length);
 	}
 
 	return -1;
@@ -141,6 +191,7 @@ void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
 	struct acpi_rhct_node_header *node, *end;
 	struct acpi_rhct_hart_info *hart_info;
 	struct acpi_table_rhct *rhct;
+	unsigned int i;
 
 	if (acpi_disabled)
 		return;
@@ -162,14 +213,25 @@ void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
 	if (cbop_size)
 		*cbop_size = 0;
 
-	end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->header.length);
-	for (node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct, rhct->node_offset);
-	     node < end;
-	     node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node, node->length)) {
+	if (!rhct_table_valid(rhct) || !rhct->node_count)
+		return;
+
+	node = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct,
+			    rhct->node_offset);
+	end = ACPI_ADD_PTR(struct acpi_rhct_node_header, rhct,
+			   rhct->header.length);
+
+	for (i = 0; i < rhct->node_count; i++) {
+		if (!rhct_node_valid(node, end))
+			return;
+
 		if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO) {
 			hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node, size_hdr);
 			acpi_parse_hart_info_cmo_node(rhct, hart_info, cbom_size,
 						      cboz_size, cbop_size);
 		}
+
+		node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node,
+				    node->length);
 	}
 }
-- 
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.