[PATCH v2 2/3] ACPI: RHCT: validate hart-info offsets and node references

Pengpeng Hou pengpeng at iscas.ac.cn
Thu Aug 13 08:57:25 PDT 2026


Each RHCT hart-info node contains a count followed by offsets to other RHCT
nodes.  Consumers trust both the offset-array extent and every referenced
address.

Require the complete offset array to fit in its hart-info node.  Resolve
each reference only when it exactly matches a validated node boundary, and
reject references to another hart-info node as required by the RHCT
definition.

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 <pengpeng at iscas.ac.cn>
---
 drivers/acpi/riscv/rhct.c | 124 +++++++++++++++++++++++++++++++-------
 1 file changed, 101 insertions(+), 23 deletions(-)

diff --git a/drivers/acpi/riscv/rhct.c b/drivers/acpi/riscv/rhct.c
index 01d6e39c0c5a..b1b850d58efd 100644
--- a/drivers/acpi/riscv/rhct.c
+++ b/drivers/acpi/riscv/rhct.c
@@ -9,6 +9,7 @@
 
 #include <linux/acpi.h>
 #include <linux/bits.h>
+#include <linux/overflow.h>
 
 static bool rhct_table_valid(struct acpi_table_rhct *rhct)
 {
@@ -48,6 +49,66 @@ static bool rhct_node_valid(struct acpi_rhct_node_header *node,
 	return true;
 }
 
+static bool rhct_node_has_data(struct acpi_rhct_node_header *node,
+			       size_t data_size)
+{
+	if (node->length < sizeof(*node) ||
+	    data_size > node->length - sizeof(*node)) {
+		pr_err(FW_BUG "Truncated RHCT node type %u\n", node->type);
+		return false;
+	}
+
+	return true;
+}
+
+static struct acpi_rhct_node_header *
+rhct_node_from_offset(struct acpi_table_rhct *rhct, u32 offset)
+{
+	struct acpi_rhct_node_header *node, *end;
+	unsigned int i;
+
+	if (!rhct_table_valid(rhct) || offset < rhct->node_offset ||
+	    offset >= rhct->header.length)
+		return NULL;
+
+	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 NULL;
+		if ((u8 *)node - (u8 *)rhct == offset)
+			return node;
+
+		node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node,
+				    node->length);
+	}
+
+	return NULL;
+}
+
+static bool rhct_hart_info_valid(struct acpi_rhct_node_header *node)
+{
+	struct acpi_rhct_hart_info *hart_info;
+	size_t offsets_size;
+
+	if (!rhct_node_has_data(node, sizeof(*hart_info)))
+		return false;
+
+	hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node,
+				 sizeof(*node));
+	if (check_mul_overflow(hart_info->num_offsets, sizeof(u32),
+			       &offsets_size) ||
+	    offsets_size > node->length - sizeof(*node) - sizeof(*hart_info)) {
+		pr_err(FW_BUG "Invalid RHCT hart-info offset array\n");
+		return false;
+	}
+
+	return true;
+}
+
 static struct acpi_table_rhct *acpi_get_rhct(void)
 {
 	static struct acpi_table_header *rhct;
@@ -77,13 +138,12 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const
 {
 	struct acpi_rhct_node_header *node, *ref_node, *end;
 	u32 size_hdr = sizeof(struct acpi_rhct_node_header);
-	u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info);
 	struct acpi_rhct_hart_info *hart_info;
 	struct acpi_rhct_isa_string *isa_node;
 	struct acpi_table_rhct *rhct;
 	u32 *hart_info_node_offset;
-	u32 acpi_cpu_id;
-	unsigned int i;
+	u32 acpi_cpu_id, ref_offset;
+	unsigned int i, j;
 	int ret;
 
 	BUG_ON(acpi_disabled);
@@ -115,12 +175,21 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const
 			return -EINVAL;
 
 		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 (!rhct_hart_info_valid(node))
+				return -EINVAL;
+
+			hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node,
+						 sizeof(*node));
+			hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info,
+							     sizeof(*hart_info));
 			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]);
+				for (j = 0; j < hart_info->num_offsets; j++) {
+					ref_offset = hart_info_node_offset[j];
+					ref_node = rhct_node_from_offset(rhct, ref_offset);
+					if (!ref_node ||
+					    ref_node->type == ACPI_RHCT_NODE_TYPE_HART_INFO)
+						return -EINVAL;
+
 					if (ref_node->type == ACPI_RHCT_NODE_TYPE_ISA_STRING) {
 						isa_node = ACPI_ADD_PTR(struct acpi_rhct_isa_string,
 									ref_node, size_hdr);
@@ -138,20 +207,30 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, unsigned int cpu, const
 	return -1;
 }
 
-static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct,
-					  struct acpi_rhct_hart_info *hart_info,
-					  u32 *cbom_size, u32 *cboz_size, u32 *cbop_size)
+static bool acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct,
+					  struct acpi_rhct_node_header *node,
+					  u32 *cbom_size, u32 *cboz_size,
+					  u32 *cbop_size)
 {
-	u32 size_hartinfo = sizeof(struct acpi_rhct_hart_info);
 	u32 size_hdr = sizeof(struct acpi_rhct_node_header);
 	struct acpi_rhct_node_header *ref_node;
+	struct acpi_rhct_hart_info *hart_info;
 	struct acpi_rhct_cmo_node *cmo_node;
 	u32 *hart_info_node_offset;
+	unsigned int i;
+
+	if (!rhct_hart_info_valid(node))
+		return false;
+
+	hart_info = ACPI_ADD_PTR(struct acpi_rhct_hart_info, node,
+				 sizeof(*node));
+	hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info,
+					     sizeof(*hart_info));
+	for (i = 0; i < hart_info->num_offsets; i++) {
+		ref_node = rhct_node_from_offset(rhct, hart_info_node_offset[i]);
+		if (!ref_node || ref_node->type == ACPI_RHCT_NODE_TYPE_HART_INFO)
+			return false;
 
-	hart_info_node_offset = ACPI_ADD_PTR(u32, hart_info, size_hartinfo);
-	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_CMO) {
 			cmo_node = ACPI_ADD_PTR(struct acpi_rhct_cmo_node,
 						ref_node, size_hdr);
@@ -177,6 +256,8 @@ static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct,
 			}
 		}
 	}
+
+	return true;
 }
 
 /*
@@ -187,9 +268,7 @@ static void acpi_parse_hart_info_cmo_node(struct acpi_table_rhct *rhct,
 void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
 			     u32 *cboz_size, u32 *cbop_size)
 {
-	u32 size_hdr = sizeof(struct acpi_rhct_node_header);
 	struct acpi_rhct_node_header *node, *end;
-	struct acpi_rhct_hart_info *hart_info;
 	struct acpi_table_rhct *rhct;
 	unsigned int i;
 
@@ -225,11 +304,10 @@ void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size,
 		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);
-		}
+		if (node->type == ACPI_RHCT_NODE_TYPE_HART_INFO &&
+		    !acpi_parse_hart_info_cmo_node(rhct, node, cbom_size,
+						   cboz_size, cbop_size))
+			return;
 
 		node = ACPI_ADD_PTR(struct acpi_rhct_node_header, node,
 				    node->length);
-- 
2.50.1 (Apple Git-155)




More information about the linux-riscv mailing list