[PATCH] efi: cper: validate processor section item bounds
From: Pengpeng Hou
Date: Mon Jul 06 2026 - 07:45:41 EST
CPER processor error sections contain counted arrays after the fixed
section header. The ARM path receives the section length, but it read
each context header size before proving that a full context header
remains. The IA32/X64 path received only the payload pointer, so it
could not bound its error information and context walks against the
section payload.
Pass the IA32/X64 payload length into the printer and track the
remaining bytes while walking variable records. Also make the ARM path
prove that a context header remains before reading its variable size
field.
Signed-off-by: Pengpeng Hou <pengpeng@xxxxxxxxxxx>
---
drivers/firmware/efi/cper-arm.c | 10 ++++++++--
drivers/firmware/efi/cper-x86.c | 32 ++++++++++++++++++++++++++++++--
drivers/firmware/efi/cper.c | 3 ++-
include/linux/cper.h | 3 ++-
4 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/drivers/firmware/efi/cper-arm.c b/drivers/firmware/efi/cper-arm.c
index b21cb1232d82..75a32f3a31a0 100644
--- a/drivers/firmware/efi/cper-arm.c
+++ b/drivers/firmware/efi/cper-arm.c
@@ -313,10 +313,16 @@ void cper_print_proc_arm(const char *pfx,
ctx_info = (struct cper_arm_ctx_info *)err_info;
max_ctx_type = ARRAY_SIZE(arm_reg_ctx_strs) - 1;
for (i = 0; i < proc->context_info_num; i++) {
- int size = ALIGN(sizeof(*ctx_info) + ctx_info->size, 16);
+ int size;
printk("%sContext info structure %d:\n", pfx, i);
- if (len < size) {
+ if (len < sizeof(*ctx_info)) {
+ pr_warn("%ssection length is too small\n", newpfx);
+ pr_warn("%sfirmware-generated error record is incorrect\n", pfx);
+ return;
+ }
+ size = ALIGN(sizeof(*ctx_info) + ctx_info->size, 16);
+ if (size > len) {
printk("%ssection length is too small\n", newpfx);
printk("%sfirmware-generated error record is incorrect\n", pfx);
return;
diff --git a/drivers/firmware/efi/cper-x86.c b/drivers/firmware/efi/cper-x86.c
index 3949d7b5e808..77621a5677da 100644
--- a/drivers/firmware/efi/cper-x86.c
+++ b/drivers/firmware/efi/cper-x86.c
@@ -3,6 +3,7 @@
#include <linux/cper.h>
#include <linux/acpi.h>
+#include <linux/printk.h>
/*
* We don't need a "CPER_IA" prefix since these are all locally defined.
@@ -254,17 +255,26 @@ static void print_err_info(const char *pfx, u8 err_type, u64 check)
}
}
-void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc)
+void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc,
+ u32 length)
{
int i;
+ u32 len;
struct cper_ia_err_info *err_info;
struct cper_ia_proc_ctx *ctx_info;
char newpfx[64], infopfx[64];
u8 err_type;
+ if (length < sizeof(*proc)) {
+ pr_warn("%ssection length is too small\n", pfx);
+ pr_warn("%sfirmware-generated error record is incorrect\n", pfx);
+ return;
+ }
+
if (proc->validation_bits & VALID_LAPIC_ID)
printk("%sLocal APIC_ID: 0x%llx\n", pfx, proc->lapic_id);
+ len = length - sizeof(*proc);
if (proc->validation_bits & VALID_CPUID_INFO) {
printk("%sCPUID Info:\n", pfx);
print_hex_dump(pfx, "", DUMP_PREFIX_OFFSET, 16, 4, proc->cpuid,
@@ -276,6 +286,11 @@ void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc)
err_info = (struct cper_ia_err_info *)(proc + 1);
for (i = 0; i < VALID_PROC_ERR_INFO_NUM(proc->validation_bits); i++) {
printk("%sError Information Structure %d:\n", pfx, i);
+ if (len < sizeof(*err_info)) {
+ pr_warn("%ssection length is too small\n", newpfx);
+ pr_warn("%sfirmware-generated error record is incorrect\n", pfx);
+ return;
+ }
err_type = cper_get_err_type(&err_info->err_type);
printk("%sError Structure Type: %s\n", newpfx,
@@ -321,14 +336,26 @@ void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc)
}
err_info++;
+ len -= sizeof(*err_info);
}
ctx_info = (struct cper_ia_proc_ctx *)err_info;
for (i = 0; i < VALID_PROC_CXT_INFO_NUM(proc->validation_bits); i++) {
- int size = ALIGN(sizeof(*ctx_info) + ctx_info->reg_arr_size, 16);
+ int size;
int groupsize = 4;
printk("%sContext Information Structure %d:\n", pfx, i);
+ if (len < sizeof(*ctx_info)) {
+ pr_warn("%ssection length is too small\n", newpfx);
+ pr_warn("%sfirmware-generated error record is incorrect\n", pfx);
+ return;
+ }
+ size = ALIGN(sizeof(*ctx_info) + ctx_info->reg_arr_size, 16);
+ if (size > len) {
+ pr_warn("%ssection length is too small\n", newpfx);
+ pr_warn("%sfirmware-generated error record is incorrect\n", pfx);
+ return;
+ }
printk("%sRegister Context Type: %s\n", newpfx,
ctx_info->reg_ctx_type < ARRAY_SIZE(ia_reg_ctx_strs) ?
@@ -357,5 +384,6 @@ void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc)
}
ctx_info = (struct cper_ia_proc_ctx *)((long)ctx_info + size);
+ len -= size;
}
}
diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index 06b4fdb59917..87e65377a15d 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -675,7 +675,8 @@ cper_estatus_print_section(const char *pfx, struct acpi_hest_generic_data *gdata
printk("%ssection_type: IA32/X64 processor error\n", newpfx);
if (gdata->error_data_length >= sizeof(*ia_err))
- cper_print_proc_ia(newpfx, ia_err);
+ cper_print_proc_ia(newpfx, ia_err,
+ gdata->error_data_length);
else
goto err_section_too_small;
#endif
diff --git a/include/linux/cper.h b/include/linux/cper.h
index 440b35e459e5..a2fa9376f6c0 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -598,7 +598,8 @@ void cper_print_proc_arm(const char *pfx,
const struct cper_sec_proc_arm *proc,
u32 length);
void cper_print_proc_ia(const char *pfx,
- const struct cper_sec_proc_ia *proc);
+ const struct cper_sec_proc_ia *proc,
+ u32 length);
int cper_mem_err_location(struct cper_mem_err_compact *mem, char *msg);
int cper_dimm_err_location(struct cper_mem_err_compact *mem, char *msg);
--
2.43.0