Skip to content
/ linux Public

Commit e0ec991

Browse files
mchehabSasha Levin
authored andcommitted
APEI/GHES: ensure that won't go past CPER allocated record
[ Upstream commit fa2408a ] The logic at ghes_new() prevents allocating too large records, by checking if they're bigger than GHES_ESTATUS_MAX_SIZE (currently, 64KB). Yet, the allocation is done with the actual number of pages from the CPER bios table location, which can be smaller. Yet, a bad firmware could send data with a different size, which might be bigger than the allocated memory, causing an OOPS: Unable to handle kernel paging request at virtual address fff00000f9b40000 Mem abort info: ESR = 0x0000000096000007 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x07: level 3 translation fault Data abort info: ISV = 0, ISS = 0x00000007, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 swapper pgtable: 4k pages, 52-bit VAs, pgdp=000000008ba16000 [fff00000f9b40000] pgd=180000013ffff403, p4d=180000013fffe403, pud=180000013f85b403, pmd=180000013f68d403, pte=0000000000000000 Internal error: Oops: 0000000096000007 [#1] SMP Modules linked in: CPU: 0 UID: 0 PID: 303 Comm: kworker/0:1 Not tainted 6.19.0-rc1-00002-gda407d200220 #34 PREEMPT Hardware name: QEMU QEMU Virtual Machine, BIOS unknown 02/02/2022 Workqueue: kacpi_notify acpi_os_execute_deferred pstate: 214020c5 (nzCv daIF +PAN -UAO -TCO +DIT -SSBS BTYPE=--) pc : hex_dump_to_buffer+0x30c/0x4a0 lr : hex_dump_to_buffer+0x328/0x4a0 sp : ffff800080e13880 x29: ffff800080e13880 x28: ffffac9aba86f6a8 x27: 0000000000000083 x26: fff00000f9b3fffc x25: 0000000000000004 x24: 0000000000000004 x23: ffff800080e13905 x22: 0000000000000010 x21: 0000000000000083 x20: 0000000000000001 x19: 0000000000000008 x18: 0000000000000010 x17: 0000000000000001 x16: 00000007c7f20fec x15: 0000000000000020 x14: 0000000000000008 x13: 0000000000081020 x12: 0000000000000008 x11: ffff800080e13905 x10: ffff800080e13988 x9 : 0000000000000000 x8 : 0000000000000000 x7 : 0000000000000001 x6 : 0000000000000020 x5 : 0000000000000030 x4 : 00000000fffffffe x3 : 0000000000000000 x2 : ffffac9aba78c1c8 x1 : ffffac9aba76d0a8 x0 : 0000000000000008 Call trace: hex_dump_to_buffer+0x30c/0x4a0 (P) print_hex_dump+0xac/0x170 cper_estatus_print_section+0x90c/0x968 cper_estatus_print+0xf0/0x158 __ghes_print_estatus+0xa0/0x148 ghes_proc+0x1bc/0x220 ghes_notify_hed+0x5c/0xb8 notifier_call_chain+0x78/0x148 blocking_notifier_call_chain+0x4c/0x80 acpi_hed_notify+0x28/0x40 acpi_ev_notify_dispatch+0x50/0x80 acpi_os_execute_deferred+0x24/0x48 process_one_work+0x15c/0x3b0 worker_thread+0x2d0/0x400 kthread+0x148/0x228 ret_from_fork+0x10/0x20 Code: 6b14033f 540001ad a94707e2 f100029f (b8747b44) ---[ end trace 0000000000000000 ]--- Prevent that by taking the actual allocated are into account when checking for CPER length. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Acked-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Hanjun Guo <guohanjun@huawei.com> [ rjw: Subject tweaks ] Link: https://patch.msgid.link/4e70310a816577fabf37d94ed36cde4ad62b1e0a.1767871950.git.mchehab+huawei@kernel.org Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent 5a9b1dd commit e0ec991

File tree

2 files changed

+6
-1
lines changed

2 files changed

+6
-1
lines changed

drivers/acpi/apei/ghes.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/timer.h>
2929
#include <linux/cper.h>
3030
#include <linux/platform_device.h>
31+
#include <linux/minmax.h>
3132
#include <linux/mutex.h>
3233
#include <linux/ratelimit.h>
3334
#include <linux/vmalloc.h>
@@ -290,6 +291,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic)
290291
error_block_length = GHES_ESTATUS_MAX_SIZE;
291292
}
292293
ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
294+
ghes->estatus_length = error_block_length;
293295
if (!ghes->estatus) {
294296
rc = -ENOMEM;
295297
goto err_unmap_status_addr;
@@ -361,13 +363,15 @@ static int __ghes_check_estatus(struct ghes *ghes,
361363
struct acpi_hest_generic_status *estatus)
362364
{
363365
u32 len = cper_estatus_len(estatus);
366+
u32 max_len = min(ghes->generic->error_block_length,
367+
ghes->estatus_length);
364368

365369
if (len < sizeof(*estatus)) {
366370
pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n");
367371
return -EIO;
368372
}
369373

370-
if (len > ghes->generic->error_block_length) {
374+
if (!len || len > max_len) {
371375
pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!\n");
372376
return -EIO;
373377
}

include/acpi/ghes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ struct ghes {
2121
struct acpi_hest_generic_v2 *generic_v2;
2222
};
2323
struct acpi_hest_generic_status *estatus;
24+
unsigned int estatus_length;
2425
unsigned long flags;
2526
union {
2627
struct list_head list;

0 commit comments

Comments
 (0)