On x86_64: When the second-stage kernel is booted via kexec with a limiting command line such as "mem=<size>" we observe a pafe fault that happens.
BUG: unable to handle page fault for address: ffff97793ff47000 RIP: ima_restore_measurement_list+0xdc/0x45a #PF: error_code(0x0000) – not-present page
This happens on x86_64 only, as this is already fixed in aarch64 in commit: cbf9c4b9617b ("of: check previous kernel's ima-kexec-buffer against memory bounds")
V1: https://lore.kernel.org/all/20251112193005.3772542-1-harshit.m.mogalapalli@o...
V1 attempted to do a similar sanity check in x86_64. Borislav suggested to add a generic helper ima_validate_range() which could then be used for both OF based and x86_64.
Testing information: -------------------- On x86_64: With latest 6.19-rc2 based, we could reproduce the issue, and patched kernel works fine. (with mem=8G on a 16G memory machine) Thanks to Yifei for finding enabling IMA_KEXEC is the cause.
Thanks for the reviews on V1.
V1 -> V2: - Patch 1: Add a generic helper "ima_validate_range()" - Patch 2: Use this new helper in drivers/of/kexec.c -> No functional change. - Patch 3: Fix the page fault by doing sanity check with "ima_validate_range()"
V2: https://lore.kernel.org/all/20251229081523.622515-1-harshit.m.mogalapalli@or...
V2 -> V3: Update subject of Patch 1 to more appropriate one (Suggested by Mimi Zohar)
Thanks, Harshit
Harshit Mogalapalli (3): ima: verify the previous kernel's IMA buffer lies in addressable RAM of/kexec: refactor ima_get_kexec_buffer() to use ima_validate_range() x86/kexec: Add a sanity check on previous kernel's ima kexec buffer
arch/x86/kernel/setup.c | 6 +++++ drivers/of/kexec.c | 15 +++---------- include/linux/ima.h | 1 + security/integrity/ima/ima_kexec.c | 35 ++++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 12 deletions(-)
When the second-stage kernel is booted with a limiting command line (e.g. "mem=<size>"), the IMA measurement buffer handed over from the previous kernel may fall outside the addressable RAM of the new kernel. Accessing such a buffer can fault during early restore.
Introduce a small generic helper, ima_validate_range(), which verifies that a physical [start, end] range for the previous-kernel IMA buffer lies within addressable memory: - On x86, use pfn_range_is_mapped(). - On OF based architectures, use page_is_ram().
Cc: stable@vger.kernel.org Signed-off-by: Harshit Mogalapalli harshit.m.mogalapalli@oracle.com --- v2->v3: Update subject to exactly describe the patch [ Suggested by Mimi Zohar] --- include/linux/ima.h | 1 + security/integrity/ima/ima_kexec.c | 35 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 8e29cb4e6a01..abf8923f8fc5 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -69,6 +69,7 @@ static inline int ima_measure_critical_data(const char *event_label, #ifdef CONFIG_HAVE_IMA_KEXEC int __init ima_free_kexec_buffer(void); int __init ima_get_kexec_buffer(void **addr, size_t *size); +int ima_validate_range(phys_addr_t phys, size_t size); #endif
#ifdef CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 7362f68f2d8b..8b24e3312ea0 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c @@ -12,6 +12,8 @@ #include <linux/kexec.h> #include <linux/of.h> #include <linux/ima.h> +#include <linux/mm.h> +#include <linux/overflow.h> #include <linux/reboot.h> #include <asm/page.h> #include "ima.h" @@ -296,3 +298,36 @@ void __init ima_load_kexec_buffer(void) pr_debug("Error restoring the measurement list: %d\n", rc); } } + +/* + * ima_validate_range - verify a physical buffer lies in addressable RAM + * @phys: physical start address of the buffer from previous kernel + * @size: size of the buffer + * + * On success return 0. On failure returns -EINVAL so callers can skip + * restoring. + */ +int ima_validate_range(phys_addr_t phys, size_t size) +{ + unsigned long start_pfn, end_pfn; + phys_addr_t end_phys; + + if (check_add_overflow(phys, (phys_addr_t)size - 1, &end_phys)) + return -EINVAL; + + start_pfn = PHYS_PFN(phys); + end_pfn = PHYS_PFN(end_phys); + +#ifdef CONFIG_X86 + if (!pfn_range_is_mapped(start_pfn, end_pfn)) +#else + if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn)) +#endif + { + pr_warn("IMA: previous kernel measurement buffer %pa (size 0x%zx) lies outside available memory\n", + &phys, size); + return -EINVAL; + } + + return 0; +}
Refactor the OF/DT ima_get_kexec_buffer() to use a generic helper to validate the address range. No functional change intended.
Cc: stable@vger.kernel.org Reviewed-by: Mimi Zohar zohar@linux.ibm.com Signed-off-by: Harshit Mogalapalli harshit.m.mogalapalli@oracle.com --- V2-> V3: Add RB from Mimi Zohar. --- drivers/of/kexec.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index 1ee2d31816ae..c4cf3552c018 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -128,7 +128,6 @@ int __init ima_get_kexec_buffer(void **addr, size_t *size) { int ret, len; unsigned long tmp_addr; - unsigned long start_pfn, end_pfn; size_t tmp_size; const void *prop;
@@ -144,17 +143,9 @@ int __init ima_get_kexec_buffer(void **addr, size_t *size) if (!tmp_size) return -ENOENT;
- /* - * Calculate the PFNs for the buffer and ensure - * they are with in addressable memory. - */ - start_pfn = PHYS_PFN(tmp_addr); - end_pfn = PHYS_PFN(tmp_addr + tmp_size - 1); - if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn)) { - pr_warn("IMA buffer at 0x%lx, size = 0x%zx beyond memory\n", - tmp_addr, tmp_size); - return -EINVAL; - } + ret = ima_validate_range(tmp_addr, tmp_size); + if (ret) + return ret;
*addr = __va(tmp_addr); *size = tmp_size;
When the second-stage kernel is booted via kexec with a limiting command line such as "mem=<size>", the physical range that contains the carried over IMA measurement list may fall outside the truncated RAM leading to a kernel panic.
BUG: unable to handle page fault for address: ffff97793ff47000 RIP: ima_restore_measurement_list+0xdc/0x45a #PF: error_code(0x0000) – not-present page
Other architectures already validate the range with page_is_ram(), as done in commit cbf9c4b9617b ("of: check previous kernel's ima-kexec-buffer against memory bounds") do a similar check on x86.
Without carrying the measurement list across kexec, the attestation would fail.
Cc: stable@vger.kernel.org Fixes: b69a2afd5afc ("x86/kexec: Carry forward IMA measurement log on kexec") Reported-by: Paul Webb paul.x.webb@oracle.com Reviewed-by: Mimi Zohar zohar@linux.ibm.com Signed-off-by: Harshit Mogalapalli harshit.m.mogalapalli@oracle.com --- V1-> V2: Added a line about carrying measure list across kexec based on suggestion from Mimi Zohar. Made use to the new generic helper [Suggestion from Borislav]
V2-> V3: Add RB from Mimi Zohar. --- arch/x86/kernel/setup.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 1b2edd07a3e1..383d4a4784f5 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -439,9 +439,15 @@ int __init ima_free_kexec_buffer(void)
int __init ima_get_kexec_buffer(void **addr, size_t *size) { + int ret; + if (!ima_kexec_buffer_size) return -ENOENT;
+ ret = ima_validate_range(ima_kexec_buffer_phys, ima_kexec_buffer_size); + if (ret) + return ret; + *addr = __va(ima_kexec_buffer_phys); *size = ima_kexec_buffer_size;
linux-stable-mirror@lists.linaro.org