linux-efi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2] arm64: Fix EFI loader kernel image allocation
@ 2021-08-02 13:10 Benjamin Herrenschmidt
  2021-08-03  5:34 ` Ard Biesheuvel
  0 siblings, 1 reply; 2+ messages in thread
From: Benjamin Herrenschmidt @ 2021-08-02 13:10 UTC (permalink / raw)
  To: linux-efi
  Cc: Javier Martinez Canillas, Peter Jones, Dimitri John Ledkov,
	Colin Watson, Saidi, Ali, benh

We are currently allocating just enough memory for the file size,
which means that the kernel BSS is in limbo (and not even zeroed).

We are also not honoring the alignment specified in the image
PE header.

This makes us use the PE optional header in which the kernel puts the
actual size it needs, including BSS, and make sure we clear it, and
honors the specified alignment for the image.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

This is v2 of the patch against Fedora/RH/Ubuntu grub2 (ie, fixes
the original shim lock introduction patch) to address random crashes
at boot on arm64.

 grub-core/loader/arm64/linux.c | 100 ++++++++++++++++++++++-----------
 1 file changed, 66 insertions(+), 34 deletions(-)

diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
index 47f8cf0d8..4a252d5e7 100644
--- a/grub-core/loader/arm64/linux.c
+++ b/grub-core/loader/arm64/linux.c
@@ -41,6 +41,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
 static grub_dl_t my_mod;
 static int loaded;
 
+static void *kernel_alloc_addr;
+static grub_uint32_t kernel_alloc_pages;
 static void *kernel_addr;
 static grub_uint64_t kernel_size;
 static grub_uint32_t handover_offset;
@@ -204,9 +206,8 @@ grub_linux_unload (void)
 			 GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
   initrd_start = initrd_end = 0;
   grub_free (linux_args);
-  if (kernel_addr)
-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+  if (kernel_alloc_addr)
+    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
   grub_fdt_unload ();
   return GRUB_ERR_NONE;
 }
@@ -311,14 +312,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   return grub_errno;
 }
 
+static grub_err_t
+parse_pe_header (void *kernel, grub_uint64_t *total_size,
+		 grub_uint32_t *entry_offset,
+		 grub_uint32_t *alignment)
+{
+  struct linux_arch_kernel_header *lh = kernel;
+  struct grub_armxx_linux_pe_header *pe;
+
+  pe = (void *)((unsigned long)kernel + lh->hdr_offset);
+
+  if (pe->opt.magic != GRUB_PE32_PE64_MAGIC)
+    return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic");
+
+  *total_size   = pe->opt.image_size;
+  *entry_offset = pe->opt.entry_addr;
+  *alignment    = pe->opt.section_alignment;
+
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 		int argc, char *argv[])
 {
   grub_file_t file = 0;
-  struct linux_arch_kernel_header lh;
-  struct grub_armxx_linux_pe_header *pe;
   grub_err_t err;
+  grub_off_t filelen;
+  grub_uint32_t align;
+  void *kernel = NULL;
   int rc;
 
   grub_dl_ref (my_mod);
@@ -333,40 +355,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
   if (!file)
     goto fail;
 
-  kernel_size = grub_file_size (file);
-
-  if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
-    return grub_errno;
-
-  if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
-    goto fail;
-
-  grub_loader_unset();
-
-  grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
-  kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
-  grub_dprintf ("linux", "kernel numpages: %lld\n",
-		(long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
-  if (!kernel_addr)
+  filelen = grub_file_size (file);
+  kernel = grub_malloc(filelen);
+  if (!kernel)
     {
-      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer"));
       goto fail;
     }
 
-  grub_file_seek (file, 0);
-  if (grub_file_read (file, kernel_addr, kernel_size)
-      < (grub_int64_t) kernel_size)
+  if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen)
     {
-      if (!grub_errno)
-	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
+      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
+		  argv[0]);
       goto fail;
     }
 
-  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
-
   if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
     {
-      rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
+      rc = grub_linuxefi_secure_validate (kernel, filelen);
       if (rc <= 0)
 	{
 	  grub_error (GRUB_ERR_INVALID_COMMAND,
@@ -375,8 +381,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
 	}
     }
 
-  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
-  handover_offset = pe->opt.entry_addr;
+  if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE)
+    goto fail;
+  if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE)
+    goto fail;
+  grub_dprintf ("linux", "kernel mem size     : %lld\n", (long long) kernel_size);
+  grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
+  grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
+
+  grub_loader_unset();
+
+  kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
+  kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages);
+  grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages);
+  if (!kernel_alloc_addr)
+    {
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      goto fail;
+    }
+  kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align);
+
+  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
+  grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size));
+  if (kernel_size > filelen)
+    grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen);
+  grub_free(kernel);
+  kernel = NULL;
 
   cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
   linux_args = grub_malloc (cmdline_size);
@@ -400,6 +430,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
     }
 
 fail:
+  if (kernel)
+    grub_free (kernel);
+
   if (file)
     grub_file_close (file);
 
@@ -412,9 +445,8 @@ fail:
   if (linux_args && !loaded)
     grub_free (linux_args);
 
-  if (kernel_addr && !loaded)
-    grub_efi_free_pages ((grub_addr_t) kernel_addr,
-			 GRUB_EFI_BYTES_TO_PAGES (kernel_size));
+  if (kernel_alloc_addr && !loaded)
+    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
 
   return grub_errno;
 }
-- 
2.25.1



^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH v2] arm64: Fix EFI loader kernel image allocation
  2021-08-02 13:10 [PATCH v2] arm64: Fix EFI loader kernel image allocation Benjamin Herrenschmidt
@ 2021-08-03  5:34 ` Ard Biesheuvel
  0 siblings, 0 replies; 2+ messages in thread
From: Ard Biesheuvel @ 2021-08-03  5:34 UTC (permalink / raw)
  To: Benjamin Herrenschmidt, Steve McIntyre
  Cc: linux-efi, Javier Martinez Canillas, Peter Jones,
	Dimitri John Ledkov, Colin Watson, Saidi, Ali, Herrenschmidt,
	Benjamin

(+ Steve)


On Mon, 2 Aug 2021 at 15:12, Benjamin Herrenschmidt
<benh@kernel.crashing.org> wrote:
>
> We are currently allocating just enough memory for the file size,
> which means that the kernel BSS is in limbo (and not even zeroed).
>
> We are also not honoring the alignment specified in the image
> PE header.
>
> This makes us use the PE optional header in which the kernel puts the
> actual size it needs, including BSS, and make sure we clear it, and
> honors the specified alignment for the image.
>
> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
> ---
>
> This is v2 of the patch against Fedora/RH/Ubuntu grub2 (ie, fixes
> the original shim lock introduction patch) to address random crashes
> at boot on arm64.
>
>  grub-core/loader/arm64/linux.c | 100 ++++++++++++++++++++++-----------
>  1 file changed, 66 insertions(+), 34 deletions(-)
>
> diff --git a/grub-core/loader/arm64/linux.c b/grub-core/loader/arm64/linux.c
> index 47f8cf0d8..4a252d5e7 100644
> --- a/grub-core/loader/arm64/linux.c
> +++ b/grub-core/loader/arm64/linux.c
> @@ -41,6 +41,8 @@ GRUB_MOD_LICENSE ("GPLv3+");
>  static grub_dl_t my_mod;
>  static int loaded;
>
> +static void *kernel_alloc_addr;
> +static grub_uint32_t kernel_alloc_pages;
>  static void *kernel_addr;
>  static grub_uint64_t kernel_size;
>  static grub_uint32_t handover_offset;
> @@ -204,9 +206,8 @@ grub_linux_unload (void)
>                          GRUB_EFI_BYTES_TO_PAGES (initrd_end - initrd_start));
>    initrd_start = initrd_end = 0;
>    grub_free (linux_args);
> -  if (kernel_addr)
> -    grub_efi_free_pages ((grub_addr_t) kernel_addr,
> -                        GRUB_EFI_BYTES_TO_PAGES (kernel_size));
> +  if (kernel_alloc_addr)
> +    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
>    grub_fdt_unload ();
>    return GRUB_ERR_NONE;
>  }
> @@ -311,14 +312,35 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
>    return grub_errno;
>  }
>
> +static grub_err_t
> +parse_pe_header (void *kernel, grub_uint64_t *total_size,
> +                grub_uint32_t *entry_offset,
> +                grub_uint32_t *alignment)
> +{
> +  struct linux_arch_kernel_header *lh = kernel;
> +  struct grub_armxx_linux_pe_header *pe;
> +
> +  pe = (void *)((unsigned long)kernel + lh->hdr_offset);
> +
> +  if (pe->opt.magic != GRUB_PE32_PE64_MAGIC)
> +    return grub_error(GRUB_ERR_BAD_OS, "Invalid PE optional header magic");
> +
> +  *total_size   = pe->opt.image_size;
> +  *entry_offset = pe->opt.entry_addr;
> +  *alignment    = pe->opt.section_alignment;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
>  static grub_err_t
>  grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
>                 int argc, char *argv[])
>  {
>    grub_file_t file = 0;
> -  struct linux_arch_kernel_header lh;
> -  struct grub_armxx_linux_pe_header *pe;
>    grub_err_t err;
> +  grub_off_t filelen;
> +  grub_uint32_t align;
> +  void *kernel = NULL;
>    int rc;
>
>    grub_dl_ref (my_mod);
> @@ -333,40 +355,24 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
>    if (!file)
>      goto fail;
>
> -  kernel_size = grub_file_size (file);
> -
> -  if (grub_file_read (file, &lh, sizeof (lh)) < (long) sizeof (lh))
> -    return grub_errno;
> -
> -  if (grub_arch_efi_linux_check_image (&lh) != GRUB_ERR_NONE)
> -    goto fail;
> -
> -  grub_loader_unset();
> -
> -  grub_dprintf ("linux", "kernel file size: %lld\n", (long long) kernel_size);
> -  kernel_addr = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (kernel_size));
> -  grub_dprintf ("linux", "kernel numpages: %lld\n",
> -               (long long) GRUB_EFI_BYTES_TO_PAGES (kernel_size));
> -  if (!kernel_addr)
> +  filelen = grub_file_size (file);
> +  kernel = grub_malloc(filelen);
> +  if (!kernel)
>      {
> -      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
> +      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel load buffer"));
>        goto fail;
>      }
>
> -  grub_file_seek (file, 0);
> -  if (grub_file_read (file, kernel_addr, kernel_size)
> -      < (grub_int64_t) kernel_size)
> +  if (grub_file_read (file, kernel, filelen) < (grub_ssize_t)filelen)
>      {
> -      if (!grub_errno)
> -       grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), argv[0]);
> +      grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
> +                 argv[0]);
>        goto fail;
>      }
>
> -  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
> -
>    if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
>      {
> -      rc = grub_linuxefi_secure_validate (kernel_addr, kernel_size);
> +      rc = grub_linuxefi_secure_validate (kernel, filelen);
>        if (rc <= 0)
>         {
>           grub_error (GRUB_ERR_INVALID_COMMAND,
> @@ -375,8 +381,32 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
>         }
>      }
>
> -  pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
> -  handover_offset = pe->opt.entry_addr;
> +  if (grub_arch_efi_linux_check_image (kernel) != GRUB_ERR_NONE)
> +    goto fail;
> +  if (parse_pe_header (kernel, &kernel_size, &handover_offset, &align) != GRUB_ERR_NONE)
> +    goto fail;
> +  grub_dprintf ("linux", "kernel mem size     : %lld\n", (long long) kernel_size);
> +  grub_dprintf ("linux", "kernel entry offset : %d\n", handover_offset);
> +  grub_dprintf ("linux", "kernel alignment    : 0x%x\n", align);
> +
> +  grub_loader_unset();
> +
> +  kernel_alloc_pages = GRUB_EFI_BYTES_TO_PAGES (kernel_size + align - 1);
> +  kernel_alloc_addr = grub_efi_allocate_any_pages (kernel_alloc_pages);
> +  grub_dprintf ("linux", "kernel numpages: %d\n", kernel_alloc_pages);
> +  if (!kernel_alloc_addr)
> +    {
> +      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
> +      goto fail;
> +    }
> +  kernel_addr = (void *)ALIGN_UP((grub_uint64_t)kernel_alloc_addr, align);
> +
> +  grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);
> +  grub_memcpy (kernel_addr, kernel, grub_min(filelen, kernel_size));
> +  if (kernel_size > filelen)
> +    grub_memset ((char *)kernel_addr + filelen, 0, kernel_size - filelen);
> +  grub_free(kernel);
> +  kernel = NULL;
>
>    cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
>    linux_args = grub_malloc (cmdline_size);
> @@ -400,6 +430,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
>      }
>
>  fail:
> +  if (kernel)
> +    grub_free (kernel);
> +
>    if (file)
>      grub_file_close (file);
>
> @@ -412,9 +445,8 @@ fail:
>    if (linux_args && !loaded)
>      grub_free (linux_args);
>
> -  if (kernel_addr && !loaded)
> -    grub_efi_free_pages ((grub_addr_t) kernel_addr,
> -                        GRUB_EFI_BYTES_TO_PAGES (kernel_size));
> +  if (kernel_alloc_addr && !loaded)
> +    grub_efi_free_pages ((grub_addr_t) kernel_alloc_addr, kernel_alloc_pages);
>
>    return grub_errno;
>  }
> --
> 2.25.1
>
>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-08-03  5:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-02 13:10 [PATCH v2] arm64: Fix EFI loader kernel image allocation Benjamin Herrenschmidt
2021-08-03  5:34 ` Ard Biesheuvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).