[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <CAMj1kXGoZ5RB4GWs_YTG7g+vGZokwe3yF-ri5BV4vOBinhqfLQ@mail.gmail.com>
Date: Thu, 27 Feb 2025 08:50:08 +0100
From: Ard Biesheuvel <ardb@...nel.org>
To: Peter Jones <pjones@...hat.com>
Cc: linux-kernel@...r.kernel.org, linux-efi@...r.kernel.org,
Lenny Szubowicz <lszubowi@...hat.com>
Subject: Re: [PATCH] efi: don't map the entire mokvar table to determine its size
On Wed, 26 Feb 2025 at 21:18, Peter Jones <pjones@...hat.com> wrote:
>
> Currently when validating the mokvar table, we (re)map the entire table
> on each iteration of the loop, adding space as we discover new entries.
> If the table grows over a certain size, this fails due to limitations of
> early_memmap(), and we get a failure and traceback:
>
> ------------[ cut here ]------------
> WARNING: CPU: 0 PID: 0 at mm/early_ioremap.c:139 __early_ioremap+0xef/0x220
> Modules linked in:
> CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.12.15-200.fc41.x86_64 #1
> Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS edk2-20250221-6.copr8698600 02/21/2025
> RIP: 0010:__early_ioremap+0xef/0x220
> Code: e5 00 f0 ff ff 48 81 e5 00 f0 ff ff 4c 89 6c 24 08 41 81 e4 ff 0f 00 00 4c 29 ed 48 89 e8 48 c1 e8 0c 41 89 c7 83 f8 40 76 04 <0f> 0b eb 82 45 6b ee c0 41 81 c5 ff 05 00 00 45 85 ff 74 36 83 3d
> RSP: 0000:ffffffff96803dd8 EFLAGS: 00010002 ORIG_RAX: 0000000000000000
> RAX: 0000000000000041 RBX: 0000000000000001 RCX: ffffffff97768250
> RDX: 8000000000000163 RSI: 0000000000000001 RDI: 000000007c4c3000
> RBP: 0000000000041000 R08: ffffffffff201630 R09: 0000000000000030
> R10: 000000007c4c3000 R11: ffffffff96803e20 R12: 0000000000000000
> R13: 000000007c4c3000 R14: 0000000000000001 R15: 0000000000000041
> FS: 0000000000000000(0000) GS:ffffffff97291000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: ffff9f1d8000040e CR3: 00000000653a4000 CR4: 00000000000000f0
> Call Trace:
> <TASK>
> ? __early_ioremap+0xef/0x220
> ? __warn.cold+0x93/0xfa
> ? __early_ioremap+0xef/0x220
> ? report_bug+0xff/0x140
> ? early_fixup_exception+0x5d/0xb0
> ? early_idt_handler_common+0x2f/0x3a
> ? __early_ioremap+0xef/0x220
> ? efi_mokvar_table_init+0xce/0x1d0
> ? setup_arch+0x864/0xc10
> ? start_kernel+0x6b/0xa10
> ? x86_64_start_reservations+0x24/0x30
> ? x86_64_start_kernel+0xed/0xf0
> ? common_startup_64+0x13e/0x141
> </TASK>
> ---[ end trace 0000000000000000 ]---
> mokvar: Failed to map EFI MOKvar config table pa=0x7c4c3000, size=265187.
>
> Mapping the entire structure isn't actually necessary, as we don't ever
> need more than one entry header mapped at once.
>
> This patch changes efi_mokvar_table_init() to only map each entry
> header, not the entire table, when determining the table size. Since
> we're not mapping any data past the variable name, it also changes the
> code to enforce that each variable name is NUL terminated, rather than
> attempting to verify it in place.
>
> Signed-off-by: Peter Jones <pjones@...hat.com>
> ---
> drivers/firmware/efi/mokvar-table.c | 41 +++++++++--------------------
> 1 file changed, 13 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/firmware/efi/mokvar-table.c b/drivers/firmware/efi/mokvar-table.c
> index 5ed0602c2f7..66eb83a0f12 100644
> --- a/drivers/firmware/efi/mokvar-table.c
> +++ b/drivers/firmware/efi/mokvar-table.c
> @@ -103,7 +103,6 @@ void __init efi_mokvar_table_init(void)
> void *va = NULL;
> unsigned long cur_offset = 0;
> unsigned long offset_limit;
> - unsigned long map_size = 0;
> unsigned long map_size_needed = 0;
> unsigned long size;
> struct efi_mokvar_table_entry *mokvar_entry;
> @@ -134,48 +133,34 @@ void __init efi_mokvar_table_init(void)
> */
> err = -EINVAL;
> while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) {
> - mokvar_entry = va + cur_offset;
> - map_size_needed = cur_offset + sizeof(*mokvar_entry);
> - if (map_size_needed > map_size) {
> - if (va)
> - early_memunmap(va, map_size);
> - /*
> - * Map a little more than the fixed size entry
> - * header, anticipating some data. It's safe to
> - * do so as long as we stay within current memory
> - * descriptor.
> - */
> - map_size = min(map_size_needed + 2*EFI_PAGE_SIZE,
> - offset_limit);
> - va = early_memremap(efi.mokvar_table, map_size);
> - if (!va) {
> - pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n",
> - efi.mokvar_table, map_size);
> - return;
> - }
> - mokvar_entry = va + cur_offset;
> + if (va)
> + early_memunmap(va, sizeof(*mokvar_entry));
> + va = early_memremap(efi.mokvar_table + cur_offset, sizeof(*mokvar_entry));
> + if (!va) {
> + pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%zu.\n",
> + efi.mokvar_table + cur_offset, sizeof(*mokvar_entry));
> + return;
> }
> + mokvar_entry = va;
>
> /* Check for last sentinel entry */
> if (mokvar_entry->name[0] == '\0') {
> if (mokvar_entry->data_size != 0)
> break;
> err = 0;
> + map_size_needed = cur_offset + sizeof(*mokvar_entry);
> break;
> }
>
> - /* Sanity check that the name is null terminated */
> - size = strnlen(mokvar_entry->name,
> - sizeof(mokvar_entry->name));
> - if (size >= sizeof(mokvar_entry->name))
> - break;
> + /* Enforce that the name is null terminated */
> + mokvar_entry->name[sizeof(mokvar_entry->name)-1] = '\0';
>
> /* Advance to the next entry */
> - cur_offset = map_size_needed + mokvar_entry->data_size;
> + cur_offset += sizeof(*mokvar_entry) + mokvar_entry->data_size;
> }
>
> if (va)
> - early_memunmap(va, map_size);
> + early_memunmap(va, sizeof(*mokvar_entry));
> if (err) {
> pr_err("EFI MOKvar config table is not valid\n");
> return;
Thanks for the fix.
Should we add something like the below to avoid mapping the same page
over and over again? Or is this premature optimization?
--- a/drivers/firmware/efi/mokvar-table.c
+++ b/drivers/firmware/efi/mokvar-table.c
@@ -99,13 +99,13 @@
*/
void __init efi_mokvar_table_init(void)
{
+ struct efi_mokvar_table_entry __aligned(1) *mokvar_entry, *next_entry;
efi_memory_desc_t md;
void *va = NULL;
unsigned long cur_offset = 0;
unsigned long offset_limit;
unsigned long map_size_needed = 0;
unsigned long size;
- struct efi_mokvar_table_entry *mokvar_entry;
int err;
if (!efi_enabled(EFI_MEMMAP))
@@ -142,7 +142,7 @@
return;
}
mokvar_entry = va;
-
+next:
/* Check for last sentinel entry */
if (mokvar_entry->name[0] == '\0') {
if (mokvar_entry->data_size != 0)
@@ -156,7 +156,19 @@ void __init efi_mokvar_table_init(void)
mokvar_entry->name[sizeof(mokvar_entry->name) - 1] = '\0';
/* Advance to the next entry */
- cur_offset += sizeof(*mokvar_entry) + mokvar_entry->data_size;
+ size = sizeof(*mokvar_entry) + mokvar_entry->data_size;
+ cur_offset += size;
+
+ /*
+ * Don't bother remapping if the current entry header and the
+ * next one end on the same page.
+ */
+ next_entry = (void *)((unsigned long)mokvar_entry + size);
+ if (((((unsigned long)(mokvar_entry + 1) - 1) ^
+ ((unsigned long)(next_entry + 1) - 1)) &
PAGE_MASK) == 0) {
+ mokvar_entry = next_entry;
+ goto next;
+ }
}
if (va)
Powered by blists - more mailing lists