[PATCH] RAS: Fix out-of-range section_length in ARM processor error handling

Abbott Liu <[email protected]>
Newsgroups org.kernel.vger.linux-edac,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Commit 87880af2d24e ("APEI/GHES: ARM processor Error: don't go past
allocated memory") added bounds checks for malformed ARM processor
error records but contained several bugs:

- In log_arm_hw_error(), the ctx_info bounds check is inverted. The
  condition `sz + (long)ctx_info - (long)err >= err->section_length`
  adds ctx_info->size when the context header is already past the end
  of the section instead of when it is within bounds. So change the
  comparison to <=.

- The vsei_len < 0 error path did not verify the pei_len and ctx_len.
  When vsei_len is negative, section_length is too small to hold the
  full record, yet pei_len and ctx_len were derived from
  err_info_num/context_info_num and may describe regions beyond the
  (long)err .. err + section_length buffer. To prevent trace_arm_event
  from reading past the allocated record, sanitize the parameters:
  move the cpu lookup above this path so it is available for tracing,
  recalculate ctx_len and pei_len based on section_length, limit them,
  set the corresponding pointers to NULL and lengths to 0  when there
  is no remaining space.

Fixes: 87880af2d24e ("APEI/GHES: ARM processor Error: don't go past allocated memory")
Signed-off-by: Abbott Liu <[email protected]>
---
 drivers/ras/ras.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/drivers/ras/ras.c b/drivers/ras/ras.c
index 03df3db62334..e37bf11d0926 100644
--- a/drivers/ras/ras.c
+++ b/drivers/ras/ras.c
@@ -58,10 +58,10 @@ void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev)
 	struct cper_arm_err_info *err_info;
 	struct cper_arm_ctx_info *ctx_info;
 	u8 *ven_err_data;
-	u32 ctx_len = 0;
+	s32 ctx_len = 0;
 	int n, sz, cpu;
 	s32 vsei_len;
-	u32 pei_len;
+	s32 pei_len;
 	u8 *pei_err, *ctx_err;
 
 	pei_len = sizeof(struct cper_arm_err_info) * err->err_info_num;
@@ -74,27 +74,37 @@ void log_arm_hw_error(struct cper_sec_proc_arm *err, const u8 sev)
 	for (n = 0; n < err->context_info_num; n++) {
 		sz = sizeof(struct cper_arm_ctx_info);
 
-		if (sz + (long)ctx_info - (long)err >= err->section_length)
+		if (sz + (long)ctx_info - (long)err <= err->section_length)
 			sz += ctx_info->size;
-
 		ctx_info = (struct cper_arm_ctx_info *)((long)ctx_info + sz);
 		ctx_len += sz;
 	}
 
+	cpu = GET_LOGICAL_INDEX(err->mpidr);
+	if (cpu < 0)
+		cpu = -1;
+
 	vsei_len = err->section_length - (sizeof(struct cper_sec_proc_arm) + pei_len + ctx_len);
 	if (vsei_len < 0) {
 		pr_warn(FW_BUG "section length: %d\n", err->section_length);
 		pr_warn(FW_BUG "section length is too small\n");
 		pr_warn(FW_BUG "firmware-generated error record is incorrect\n");
 		vsei_len = 0;
-	}
-	ven_err_data = (u8 *)ctx_info;
-
-	cpu = GET_LOGICAL_INDEX(err->mpidr);
-	if (cpu < 0)
-		cpu = -1;
-
-	trace_arm_event(err, pei_err, pei_len, ctx_err, ctx_len,
+		ven_err_data = NULL;
+		ctx_len = err->section_length - (sizeof(struct cper_sec_proc_arm) + pei_len);
+		if (ctx_len < 0) {
+			ctx_len = 0;
+			ctx_err = NULL;
+			pei_len = err->section_length - sizeof(struct cper_sec_proc_arm);
+			if (pei_len < 0) {
+				pei_len = 0;
+				pei_err = NULL;
+			}
+		}
+	} else
+		ven_err_data = (u8 *)ctx_info;
+
+	trace_arm_event(err, pei_err, (u32)pei_len, ctx_err, (u32)ctx_len,
 			ven_err_data, (u32)vsei_len, sev, cpu);
 }
 
-- 
2.43.0
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.