[PATCH v2 2/2] efi/cper: bound IA32/X64 processor record walks

Pengpeng Hou <[email protected]>
Newsgroups org.kernel.vger.linux-acpi,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
IA32/X64 processor CPER sections encode fixed error-info and variable
context record counts in validation_bits.  cper_print_proc_ia() receives
only a pointer, so a truncated section can make either loop dereference a
missing header or dump a register array beyond the section.

Pass the section length to the printer and track the unconsumed bytes after
the fixed processor header.  Require every fixed record, variable payload
and aligned context extent to fit before reading it.

Fixes: f9e1bdb9f35f ("efi: Decode IA32/X64 Processor Error Section")

Assisted-by: Codex:gpt-5
Signed-off-by: Pengpeng Hou <[email protected]>
---
 drivers/firmware/efi/cper-x86.c | 51 +++++++++++++++++++++++++++++++++++++--
 drivers/firmware/efi/cper.c     |  3 ++-
 include/linux/cper.h            |  3 ++-
 3 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/drivers/firmware/efi/cper-x86.c b/drivers/firmware/efi/cper-x86.c
index 3949d7b5e808..fd0e84cf836f 100644
--- a/drivers/firmware/efi/cper-x86.c
+++ b/drivers/firmware/efi/cper-x86.c
@@ -2,6 +2,7 @@
 // Copyright (C) 2018, Advanced Micro Devices, Inc.
 
 #include <linux/cper.h>
+#include <linux/limits.h>
 #include <linux/acpi.h>
 
 /*
@@ -254,14 +254,24 @@ 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;
 	struct cper_ia_err_info *err_info;
 	struct cper_ia_proc_ctx *ctx_info;
 	char newpfx[64], infopfx[64];
+	size_t len;
 	u8 err_type;
 
+	if (length < sizeof(*proc)) {
+		printk("%ssection length is too small\n", pfx);
+		printk("%sfirmware-generated error record is incorrect\n", pfx);
+		return;
+	}
+
+	len = length - sizeof(*proc);
+
 	if (proc->validation_bits & VALID_LAPIC_ID)
 		printk("%sLocal APIC_ID: 0x%llx\n", pfx, proc->lapic_id);
 
@@ -275,6 +285,12 @@ 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++) {
+		if (len < sizeof(*err_info)) {
+			printk("%ssection length is too small\n", newpfx);
+			printk("%sfirmware-generated error record is incorrect\n", pfx);
+			return;
+		}
+
 		printk("%sError Information Structure %d:\n", pfx, i);
 
 		err_type = cper_get_err_type(&err_info->err_type);
@@ -321,13 +337,40 @@ 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);
+		size_t size;
 		int groupsize = 4;
 
+		if (len < sizeof(*ctx_info)) {
+			printk("%ssection length is too small\n", newpfx);
+			printk("%sfirmware-generated error record is incorrect\n", pfx);
+			return;
+		}
+
+		if (ctx_info->reg_arr_size > len - sizeof(*ctx_info)) {
+			printk("%ssection length is too small\n", newpfx);
+			printk("%sfirmware-generated error record is incorrect\n", pfx);
+			return;
+		}
+
+		size = sizeof(*ctx_info) + ctx_info->reg_arr_size;
+		if (size > SIZE_MAX - 15) {
+			printk("%ssection length is too large\n", newpfx);
+			printk("%sfirmware-generated error record is incorrect\n", pfx);
+			return;
+		}
+
+		size = ALIGN(size, 16);
+		if (size > len) {
+			printk("%ssection length is too small\n", newpfx);
+			printk("%sfirmware-generated error record is incorrect\n", pfx);
+			return;
+		}
+
 		printk("%sContext Information Structure %d:\n", pfx, i);
 
 		printk("%sRegister Context Type: %s\n", newpfx,
@@ -356,6 +399,7 @@ void cper_print_proc_ia(const char *pfx, const struct cper_sec_proc_ia *proc)
 				       ctx_info->reg_arr_size, 0);
 		}
 
-		ctx_info = (struct cper_ia_proc_ctx *)((long)ctx_info + size);
+		ctx_info = (struct cper_ia_proc_ctx *)((u8 *)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.50.1 (Apple Git-155)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.