Re: [PATCH] efi: cper: validate processor section item bounds

From: Ard Biesheuvel

Date: Sat Aug 01 2026 - 09:05:09 EST




On Mon, 6 Jul 2026, at 11:31, Pengpeng Hou wrote:
> 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.
>

Please split this into two patches (or more), and explain in more detail
what the problem is and how you are fixing it.


> 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