[PATCH v2 1/3] ACPI: RHCT: validate table and node extents before traversal
Pengpeng Hou
pengpeng at iscas.ac.cn
Thu Aug 13 08:48:57 PDT 2026
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 <pengpeng at iscas.ac.cn>
---
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)
More information about the linux-riscv
mailing list