[PATCH v2 2/3] ACPI: RIMT: validate node payload and array extents
Pengpeng Hou
pengpeng at iscas.ac.cn
Thu Aug 13 08:43:03 PDT 2026
RIMT node lengths bound type-specific payloads, but the parser casts
node_data and follows interrupt-wire and ID-mapping offsets without proving
their extents. Platform-device matching also treats the firmware name as
an unbounded C string.
Require each supported fixed payload and non-empty array to fit in its
node, and require a platform device name to terminate before the mapping
array or node end.
Fixes: 8f7729552582 ("ACPI: RISC-V: Add support for RIMT")
Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <pengpeng at iscas.ac.cn>
---
drivers/acpi/riscv/rimt.c | 107 ++++++++++++++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
diff --git a/drivers/acpi/riscv/rimt.c b/drivers/acpi/riscv/rimt.c
index 533e9aba6e97..e8075f79b552 100644
--- a/drivers/acpi/riscv/rimt.c
+++ b/drivers/acpi/riscv/rimt.c
@@ -12,8 +12,10 @@
#include <linux/device/driver.h>
#include <linux/iommu.h>
#include <linux/list.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/string.h>
#include "init.h"
struct rimt_fwnode {
@@ -70,6 +72,99 @@ static bool rimt_node_valid(struct acpi_rimt_node *node,
return true;
}
+static bool rimt_node_has_data(struct acpi_rimt_node *node, size_t data_size)
+{
+ if (node->length < sizeof(*node) ||
+ data_size > node->length - sizeof(*node)) {
+ pr_err(FW_BUG "Truncated RIMT node type %u\n", node->type);
+ return false;
+ }
+
+ return true;
+}
+
+static bool rimt_node_array_valid(struct acpi_rimt_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 < min_offset || offset > node->length ||
+ check_mul_overflow(count, elem_size, &bytes) ||
+ bytes > node->length - offset) {
+ pr_err(FW_BUG "Invalid %s in RIMT node type %u\n", name,
+ node->type);
+ return false;
+ }
+
+ return true;
+}
+
+static bool rimt_node_data_valid(struct acpi_rimt_node *node)
+{
+ size_t data_offset = sizeof(*node);
+
+ switch (node->type) {
+ case ACPI_RIMT_NODE_TYPE_IOMMU: {
+ struct acpi_rimt_iommu *iommu;
+
+ if (!rimt_node_has_data(node, sizeof(*iommu)))
+ return false;
+
+ iommu = (struct acpi_rimt_iommu *)node->node_data;
+ return rimt_node_array_valid(node, iommu->interrupt_wire_offset,
+ iommu->num_interrupt_wires,
+ sizeof(struct acpi_rimt_iommu_wire_gsi),
+ data_offset + sizeof(*iommu),
+ "interrupt wire array");
+ }
+ case ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX: {
+ struct acpi_rimt_pcie_rc *pci_rc;
+
+ if (!rimt_node_has_data(node, sizeof(*pci_rc)))
+ return false;
+
+ pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data;
+ return rimt_node_array_valid(node, pci_rc->id_mapping_offset,
+ pci_rc->num_id_mappings,
+ sizeof(struct acpi_rimt_id_mapping),
+ data_offset + sizeof(*pci_rc),
+ "ID mapping array");
+ }
+ case ACPI_RIMT_NODE_TYPE_PLAT_DEVICE: {
+ struct acpi_rimt_platform_device *plat;
+ size_t name_end;
+
+ if (!rimt_node_has_data(node, sizeof(*plat)))
+ return false;
+
+ plat = (struct acpi_rimt_platform_device *)node->node_data;
+ if (!rimt_node_array_valid(node, plat->id_mapping_offset,
+ plat->num_id_mappings,
+ sizeof(struct acpi_rimt_id_mapping),
+ data_offset + sizeof(*plat),
+ "ID mapping array"))
+ return false;
+
+ name_end = plat->num_id_mappings ? plat->id_mapping_offset :
+ node->length;
+ if (name_end <= data_offset + sizeof(*plat) ||
+ !memchr(plat->device_name, '\0',
+ name_end - data_offset - sizeof(*plat))) {
+ pr_err(FW_BUG "Invalid device name in RIMT node\n");
+ return false;
+ }
+
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
/**
* rimt_set_fwnode() - Create rimt_fwnode and use it to register
* iommu data in the rimt_fwnode_list
@@ -107,6 +202,9 @@ static acpi_status rimt_match_node_callback(struct acpi_rimt_node *node,
acpi_status status = AE_NOT_FOUND;
struct device *dev = context;
+ if (!rimt_node_data_valid(node))
+ return status;
+
if (node->type == ACPI_RIMT_NODE_TYPE_IOMMU) {
struct acpi_rimt_iommu *iommu_node = (struct acpi_rimt_iommu *)&node->node_data;
@@ -285,6 +383,9 @@ static bool rimt_pcie_rc_supports_ats(struct acpi_rimt_node *node)
{
struct acpi_rimt_pcie_rc *pci_rc;
+ if (!rimt_node_data_valid(node))
+ return false;
+
pci_rc = (struct acpi_rimt_pcie_rc *)node->node_data;
return pci_rc->flags & ACPI_RIMT_PCIE_ATS_SUPPORTED;
}
@@ -340,6 +441,9 @@ static struct acpi_rimt_node *rimt_node_get_id(struct acpi_rimt_node *node,
struct acpi_rimt_id_mapping *map;
struct acpi_rimt_node *parent;
+ if (index < 0 || !rimt_node_data_valid(node))
+ return NULL;
+
if (node->type == ACPI_RIMT_NODE_TYPE_PCIE_ROOT_COMPLEX) {
pci_node = (struct acpi_rimt_pcie_rc *)&node->node_data;
id_mapping_offset = pci_node->id_mapping_offset;
@@ -391,6 +495,9 @@ static struct acpi_rimt_node *rimt_node_map_id(struct acpi_rimt_node *node,
int i, rc = 0;
u32 map_id = id;
+ if (!rimt_node_data_valid(node))
+ goto fail_map;
+
if (RIMT_TYPE_MASK(node->type) & type_mask) {
if (id_out)
*id_out = id;
--
2.50.1 (Apple Git-155)
More information about the linux-riscv
mailing list