From 4c66fd1b0d03ecdbb5cf363970c555558a6987d2 Mon Sep 17 00:00:00 2001 From: James Morse Date: Tue, 29 Jan 2019 18:48:54 +0000 Subject: [PATCH] BACKPORT: ACPI / APEI: Make GHES estatus header validation more user friendly ghes_read_estatus() checks various lengths in the top-level header to ensure the CPER records to be read aren't obviously corrupt. Take the opportunity to make this more user-friendly, printing a (ratelimited) message about the nature of the header format error. This patch is needed because Quicksilver firmware-first error handling uses the SDEI notification type for communication between trusted firmware and the OS. This adds needed NMI and SDEI functionality so that the SDEI path in the kernel through APEI acts as an NMI and is properly wired up to the APEI interfaces. Backported from: https://github.com/torvalds/linux/commit/f2a681b9160b9c80826b3062e71371cfc82b4863 Suggested-by: Borislav Petkov Signed-off-by: James Morse Signed-off-by: Tyler Baicar --- drivers/acpi/apei/ghes.c | 46 ++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index f95db2398..9391fff71 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -293,6 +293,30 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, } } +/* Check the top-level record header has an appropriate size. */ +int __ghes_check_estatus(struct ghes *ghes, + struct acpi_hest_generic_status *estatus) +{ + u32 len = cper_estatus_len(estatus); + + if (len < sizeof(*estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Truncated error status block!\n"); + return -EIO; + } + + if (len > ghes->generic->error_block_length) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid error status block length!\n"); + return -EIO; + } + + if (cper_estatus_check_header(estatus)) { + pr_warn_ratelimited(FW_WARN GHES_PFX "Invalid CPER header!\n"); + return -EIO; + } + + return 0; +} + static int ghes_read_estatus(struct ghes *ghes, struct acpi_hest_generic_status *estatus, u64 *buf_paddr, enum fixed_addresses fixmap_idx) @@ -319,27 +343,21 @@ static int ghes_read_estatus(struct ghes *ghes, return -ENOENT; } - rc = -EIO; + rc = __ghes_check_estatus(ghes, estatus); + if (rc) + return rc; + len = cper_estatus_len(estatus); - if (len < sizeof(*estatus)) - goto err_read_block; - if (len > ghes->generic->error_block_length) - goto err_read_block; - if (cper_estatus_check_header(estatus)) - goto err_read_block; ghes_copy_tofrom_phys(estatus + 1, *buf_paddr + sizeof(*estatus), len - sizeof(*estatus), 1, fixmap_idx); - if (cper_estatus_check(estatus)) - goto err_read_block; - rc = 0; - -err_read_block: - if (rc) + if (cper_estatus_check(estatus)) { pr_warn_ratelimited(FW_WARN GHES_PFX "Failed to read error status block!\n"); + return -EIO; + } - return rc; + return 0; } static void ghes_clear_estatus(struct ghes *ghes,