* [PATCH] ACPI: IORT: validate RMR node array bounds
@ 2026-07-06 9:43 Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Pengpeng Hou @ 2026-07-06 9:43 UTC (permalink / raw)
To: Lorenzo Pieralisi
Cc: Pengpeng Hou, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J. Wysocki, Len Brown, linux-acpi,
linux-arm-kernel, linux-kernel
IORT RMR nodes describe reserved-memory ranges through firmware
offset and count fields inside the current IORT node.
Validate the generic IORT node length before dispatching it, and
check both the RMR descriptor array and the ID mapping array before
walking them. This binds each array walk to the current node length
instead of only trusting the firmware-provided count.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
1 file changed, 79 insertions(+), 4 deletions(-)
diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index af7a9b2fd5bc..55373246bdd4 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -15,6 +15,7 @@
#include <linux/iommu.h>
#include <linux/kernel.h>
#include <linux/list.h>
+#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -148,6 +149,69 @@ typedef acpi_status (*iort_find_node_callback)
/* Root pointer to the mapped IORT table */
static struct acpi_table_header *iort_table;
+static bool iort_node_valid(struct acpi_iort_node *node,
+ struct acpi_iort_node *end)
+{
+ size_t remaining;
+
+ if (WARN_TAINT(node >= end, TAINT_FIRMWARE_WORKAROUND,
+ "IORT node pointer overflows, bad table!\n"))
+ return false;
+
+ remaining = (char *)end - (char *)node;
+ if (WARN_TAINT(remaining < sizeof(*node), TAINT_FIRMWARE_WORKAROUND,
+ "IORT node header is truncated, bad table!\n"))
+ return false;
+
+ if (WARN_TAINT(node->length < sizeof(*node) || node->length > remaining,
+ TAINT_FIRMWARE_WORKAROUND,
+ "IORT node length overflows, bad table!\n"))
+ return false;
+
+ return true;
+}
+
+static bool iort_node_array_valid(struct acpi_iort_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 || offset < min_offset || offset > node->length) {
+ pr_err(FW_BUG "Invalid %s offset in IORT node %p\n", name,
+ node);
+ return false;
+ }
+
+ if (check_mul_overflow(count, elem_size, &bytes) ||
+ bytes > node->length - offset) {
+ pr_err(FW_BUG "Invalid %s array in IORT node %p\n", name,
+ node);
+ return false;
+ }
+
+ return true;
+}
+
+static bool iort_rmr_node_valid(struct acpi_iort_node *node)
+{
+ struct acpi_iort_rmr *rmr;
+
+ if (node->length < sizeof(*node) + sizeof(*rmr)) {
+ pr_err(FW_BUG "Truncated RMR node in IORT table\n");
+ return false;
+ }
+
+ rmr = (struct acpi_iort_rmr *)node->node_data;
+ return iort_node_array_valid(node, rmr->rmr_offset, rmr->rmr_count,
+ sizeof(struct acpi_iort_rmr_desc),
+ sizeof(*node) + sizeof(*rmr),
+ "RMR descriptor");
+}
+
static LIST_HEAD(iort_msi_chip_list);
static DEFINE_SPINLOCK(iort_msi_chip_lock);
@@ -243,8 +307,7 @@ static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
iort_table->length);
for (i = 0; i < iort->node_count; i++) {
- if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
- "IORT node pointer overflows, bad table!\n"))
+ if (!iort_node_valid(iort_node, iort_end))
return NULL;
if (iort_node->type == type &&
@@ -1014,6 +1077,9 @@ static void iort_get_rmrs(struct acpi_iort_node *node,
struct acpi_iort_rmr_desc *rmr_desc;
int i;
+ if (!iort_rmr_node_valid(node))
+ return;
+
rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, node,
rmr->rmr_offset);
@@ -1114,6 +1180,16 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
return;
}
+ if (!iort_node_array_valid(node, node->mapping_offset,
+ node->mapping_count,
+ sizeof(struct acpi_iort_id_mapping),
+ sizeof(*node),
+ "ID mapping"))
+ return;
+
+ if (!iort_rmr_node_valid(node))
+ return;
+
rmr = (struct acpi_iort_rmr *)node->node_data;
if (!rmr->rmr_offset || !rmr->rmr_count)
return;
@@ -1175,8 +1251,7 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
iort_table->length);
for (i = 0; i < iort->node_count; i++) {
- if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
- "IORT node pointer overflows, bad table!\n"))
+ if (!iort_node_valid(iort_node, iort_end))
return;
if (iort_node->type == ACPI_IORT_NODE_RMR)
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* Re: [PATCH] ACPI: IORT: validate RMR node array bounds
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
@ 2026-07-31 15:42 ` Will Deacon
2026-08-04 3:40 ` Hanjun Guo
2026-08-04 7:07 ` Hanjun Guo
2026-08-04 22:44 ` kernel test robot
2 siblings, 1 reply; 5+ messages in thread
From: Will Deacon @ 2026-07-31 15:42 UTC (permalink / raw)
To: Pengpeng Hou
Cc: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Rafael J. Wysocki, Len Brown, linux-acpi, linux-arm-kernel,
linux-kernel
On Mon, Jul 06, 2026 at 05:43:00PM +0800, Pengpeng Hou wrote:
> IORT RMR nodes describe reserved-memory ranges through firmware
> offset and count fields inside the current IORT node.
>
> Validate the generic IORT node length before dispatching it, and
> check both the RMR descriptor array and the ID mapping array before
> walking them. This binds each array walk to the current node length
> instead of only trusting the firmware-provided count.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 79 insertions(+), 4 deletions(-)
Hanjun, Lorenzo, any thoughts on this one?
Will
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] ACPI: IORT: validate RMR node array bounds
2026-07-31 15:42 ` Will Deacon
@ 2026-08-04 3:40 ` Hanjun Guo
0 siblings, 0 replies; 5+ messages in thread
From: Hanjun Guo @ 2026-08-04 3:40 UTC (permalink / raw)
To: Will Deacon, Pengpeng Hou
Cc: Lorenzo Pieralisi, Sudeep Holla, Catalin Marinas,
Rafael J. Wysocki, Len Brown, linux-acpi, linux-arm-kernel,
linux-kernel
Hi Will,
On 2026/7/31 23:42, Will Deacon wrote:
> On Mon, Jul 06, 2026 at 05:43:00PM +0800, Pengpeng Hou wrote:
>> IORT RMR nodes describe reserved-memory ranges through firmware
>> offset and count fields inside the current IORT node.
>>
>> Validate the generic IORT node length before dispatching it, and
>> check both the RMR descriptor array and the ID mapping array before
>> walking them. This binds each array walk to the current node length
>> instead of only trusting the firmware-provided count.
>>
>> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
>> ---
>> drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 79 insertions(+), 4 deletions(-)
>
> Hanjun, Lorenzo, any thoughts on this one?
Thanks for reminding.
In my personal opinion, this is not a real issue because if something
wrong with the IORT, the IORT will not work at booting, for example
the device will map to a wrong ITS or SMMU, the device will not work
at all, so those issues will be fixed at the pre-production stage,
correct me if I'm wrong.
But to the patch itself, it's no harm, I will comment on this patch.
Thanks
Hanjun
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] ACPI: IORT: validate RMR node array bounds
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
@ 2026-08-04 7:07 ` Hanjun Guo
2026-08-04 22:44 ` kernel test robot
2 siblings, 0 replies; 5+ messages in thread
From: Hanjun Guo @ 2026-08-04 7:07 UTC (permalink / raw)
To: Pengpeng Hou, Lorenzo Pieralisi
Cc: Sudeep Holla, Catalin Marinas, Will Deacon, Rafael J. Wysocki,
Len Brown, linux-acpi, linux-arm-kernel, linux-kernel
On 2026/7/6 17:43, Pengpeng Hou wrote:
> IORT RMR nodes describe reserved-memory ranges through firmware
> offset and count fields inside the current IORT node.
>
> Validate the generic IORT node length before dispatching it, and
> check both the RMR descriptor array and the ID mapping array before
> walking them. This binds each array walk to the current node length
> instead of only trusting the firmware-provided count.
Would you mind split this into two patches? one for iort_node_valid(),
the other is valid the IORT RMR nodes.
>
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> drivers/acpi/arm64/iort.c | 83 +++++++++++++++++++++++++++++++++++++--
> 1 file changed, 79 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
> index af7a9b2fd5bc..55373246bdd4 100644
> --- a/drivers/acpi/arm64/iort.c
> +++ b/drivers/acpi/arm64/iort.c
> @@ -15,6 +15,7 @@
> #include <linux/iommu.h>
> #include <linux/kernel.h>
> #include <linux/list.h>
> +#include <linux/overflow.h>
> #include <linux/pci.h>
> #include <linux/platform_device.h>
> #include <linux/slab.h>
> @@ -148,6 +149,69 @@ typedef acpi_status (*iort_find_node_callback)
> /* Root pointer to the mapped IORT table */
> static struct acpi_table_header *iort_table;
>
> +static bool iort_node_valid(struct acpi_iort_node *node,
> + struct acpi_iort_node *end)
> +{
> + size_t remaining;
> +
> + if (WARN_TAINT(node >= end, TAINT_FIRMWARE_WORKAROUND,
> + "IORT node pointer overflows, bad table!\n"))
> + return false;
> +
> + remaining = (char *)end - (char *)node;
> + if (WARN_TAINT(remaining < sizeof(*node), TAINT_FIRMWARE_WORKAROUND,
> + "IORT node header is truncated, bad table!\n"))
> + return false;
> +
> + if (WARN_TAINT(node->length < sizeof(*node) || node->length > remaining,
Check the node length as well.
> + TAINT_FIRMWARE_WORKAROUND,
> + "IORT node length overflows, bad table!\n"))
> + return false;
> +
> + return true;
> +}
> +
> +static bool iort_node_array_valid(struct acpi_iort_node *node, u32 offset,
> + u32 count, size_t elem_size,
> + size_t min_offset, const char *name)
> +{
> + size_t bytes;
> +
> + if (!count)
> + return true;
Please add a comment here why count = 0 is valid.
> +
> + if (!offset || offset < min_offset || offset > node->length) {
> + pr_err(FW_BUG "Invalid %s offset in IORT node %p\n", name,
> + node);
> + return false;
> + }
> +
> + if (check_mul_overflow(count, elem_size, &bytes) ||
> + bytes > node->length - offset) {
> + pr_err(FW_BUG "Invalid %s array in IORT node %p\n", name,
> + node);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool iort_rmr_node_valid(struct acpi_iort_node *node)
> +{
> + struct acpi_iort_rmr *rmr;
> +
> + if (node->length < sizeof(*node) + sizeof(*rmr)) {
> + pr_err(FW_BUG "Truncated RMR node in IORT table\n");
> + return false;
> + }
> +
> + rmr = (struct acpi_iort_rmr *)node->node_data;
> + return iort_node_array_valid(node, rmr->rmr_offset, rmr->rmr_count,
> + sizeof(struct acpi_iort_rmr_desc),
> + sizeof(*node) + sizeof(*rmr),
> + "RMR descriptor");
> +}
> +
> static LIST_HEAD(iort_msi_chip_list);
> static DEFINE_SPINLOCK(iort_msi_chip_lock);
>
> @@ -243,8 +307,7 @@ static struct acpi_iort_node *iort_scan_node(enum acpi_iort_node_type type,
> iort_table->length);
>
> for (i = 0; i < iort->node_count; i++) {
> - if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
> - "IORT node pointer overflows, bad table!\n"))
> + if (!iort_node_valid(iort_node, iort_end))
> return NULL;
>
> if (iort_node->type == type &&
> @@ -1014,6 +1077,9 @@ static void iort_get_rmrs(struct acpi_iort_node *node,
> struct acpi_iort_rmr_desc *rmr_desc;
> int i;
>
> + if (!iort_rmr_node_valid(node))
> + return;
> +
> rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, node,
> rmr->rmr_offset);
>
> @@ -1114,6 +1180,16 @@ static void iort_node_get_rmr_info(struct acpi_iort_node *node,
> return;
> }
>
> + if (!iort_node_array_valid(node, node->mapping_offset,
> + node->mapping_count,
> + sizeof(struct acpi_iort_id_mapping),
> + sizeof(*node),
> + "ID mapping"))
> + return;
> +
> + if (!iort_rmr_node_valid(node))
> + return;
> +
> rmr = (struct acpi_iort_rmr *)node->node_data;
> if (!rmr->rmr_offset || !rmr->rmr_count)
> return;
> @@ -1175,8 +1251,7 @@ static void iort_find_rmrs(struct acpi_iort_node *iommu, struct device *dev,
> iort_table->length);
>
> for (i = 0; i < iort->node_count; i++) {
> - if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND,
> - "IORT node pointer overflows, bad table!\n"))
> + if (!iort_node_valid(iort_node, iort_end))
> return;
>
> if (iort_node->type == ACPI_IORT_NODE_RMR)
Thanks
Hanjun
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: [PATCH] ACPI: IORT: validate RMR node array bounds
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
2026-08-04 7:07 ` Hanjun Guo
@ 2026-08-04 22:44 ` kernel test robot
2 siblings, 0 replies; 5+ messages in thread
From: kernel test robot @ 2026-08-04 22:44 UTC (permalink / raw)
To: Pengpeng Hou, Lorenzo Pieralisi
Cc: oe-kbuild-all, Pengpeng Hou, Hanjun Guo, Sudeep Holla,
Catalin Marinas, Will Deacon, Rafael J. Wysocki, Len Brown,
linux-acpi, linux-arm-kernel, linux-kernel
Hi Pengpeng,
kernel test robot noticed the following build warnings:
[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on rafael-pm/bleeding-edge linus/master v7.2-rc6 next-20260804]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Pengpeng-Hou/ACPI-IORT-validate-RMR-node-array-bounds/20260804-123552
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link: https://lore.kernel.org/r/20260706094300.82618-1-pengpeng%40iscas.ac.cn
patch subject: [PATCH] ACPI: IORT: validate RMR node array bounds
config: arm64-allnoconfig-bpf (https://download.01.org/0day-ci/archive/20260805/202608050045.wC3xEg9O-lkp@intel.com/config)
compiler: aarch64-linux-gnu-gcc (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260805/202608050045.wC3xEg9O-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202608050045.wC3xEg9O-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/acpi/arm64/iort.c:199:13: warning: 'iort_rmr_node_valid' defined but not used [-Wunused-function]
199 | static bool iort_rmr_node_valid(struct acpi_iort_node *node)
| ^~~~~~~~~~~~~~~~~~~
vim +/iort_rmr_node_valid +199 drivers/acpi/arm64/iort.c
198
> 199 static bool iort_rmr_node_valid(struct acpi_iort_node *node)
200 {
201 struct acpi_iort_rmr *rmr;
202
203 if (node->length < sizeof(*node) + sizeof(*rmr)) {
204 pr_err(FW_BUG "Truncated RMR node in IORT table\n");
205 return false;
206 }
207
208 rmr = (struct acpi_iort_rmr *)node->node_data;
209 return iort_node_array_valid(node, rmr->rmr_offset, rmr->rmr_count,
210 sizeof(struct acpi_iort_rmr_desc),
211 sizeof(*node) + sizeof(*rmr),
212 "RMR descriptor");
213 }
214
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-04 22:44 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06 9:43 [PATCH] ACPI: IORT: validate RMR node array bounds Pengpeng Hou
2026-07-31 15:42 ` Will Deacon
2026-08-04 3:40 ` Hanjun Guo
2026-08-04 7:07 ` Hanjun Guo
2026-08-04 22:44 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox