Blame SOURCES/0511-loader-i386-efi-linux-Use-grub_loader_set_ex.patch

bf0270
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
bf0270
From: Chris Coulson <chris.coulson@canonical.com>
bf0270
Date: Mon, 2 May 2022 17:04:23 +0200
bf0270
Subject: [PATCH] loader/i386/efi/linux: Use grub_loader_set_ex
bf0270
bf0270
This ports the linuxefi loader to use grub_loader_set_ex in order to fix
bf0270
a use-after-fre bug that occurs when grub_cmd_linux is executed more than
bf0270
once before a boot attempt is performed.
bf0270
bf0270
This is more complicated than for the chainloader command, as the initrd
bf0270
command needs access to the loader state. To solve this, the linuxefi
bf0270
module registers a dummy initrd command at startup that returns an error.
bf0270
The linuxefi command then registers a proper initrd command with a higher
bf0270
priority that is passed the loader state.
bf0270
bf0270
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
bf0270
(cherry picked from commit 7cf736436b4c934df5ddfa6f44b46a7e07d99fdc)
bf0270
[rharwood/pjones: set kernel_size in context]
bf0270
(cherry picked from commit 9c056391f7a36ea480de9a759c12e55a90f2040a)
bf0270
[rharwood: verifying twice]
bf0270
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
bf0270
(cherry picked from commit df804892f1a754d88a9779320f9429bf40d2a1b3)
bf0270
---
bf0270
 grub-core/loader/i386/efi/linux.c | 146 +++++++++++++++++++++++---------------
bf0270
 1 file changed, 87 insertions(+), 59 deletions(-)
bf0270
bf0270
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
bf0270
index c9a2b47370..77a0734786 100644
bf0270
--- a/grub-core/loader/i386/efi/linux.c
bf0270
+++ b/grub-core/loader/i386/efi/linux.c
bf0270
@@ -34,13 +34,19 @@
bf0270
 GRUB_MOD_LICENSE ("GPLv3+");
bf0270
 
bf0270
 static grub_dl_t my_mod;
bf0270
-static int loaded;
bf0270
-static void *kernel_mem;
bf0270
-static grub_uint64_t kernel_size;
bf0270
-static void *initrd_mem;
bf0270
-static grub_uint32_t handover_offset;
bf0270
-struct linux_kernel_params *params;
bf0270
-static char *linux_cmdline;
bf0270
+
bf0270
+static grub_command_t cmd_linux, cmd_initrd;
bf0270
+static grub_command_t cmd_linuxefi, cmd_initrdefi;
bf0270
+
bf0270
+struct grub_linuxefi_context {
bf0270
+  void *kernel_mem;
bf0270
+  grub_uint64_t kernel_size;
bf0270
+  grub_uint32_t handover_offset;
bf0270
+  struct linux_kernel_params *params;
bf0270
+  char *cmdline;
bf0270
+
bf0270
+  void *initrd_mem;
bf0270
+};
bf0270
 
bf0270
 #define MIN(a, b) \
bf0270
   ({ typeof (a) _a = (a); \
bf0270
@@ -123,25 +129,32 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
bf0270
 }
bf0270
 
bf0270
 static grub_err_t
bf0270
-grub_linuxefi_boot (void)
bf0270
+grub_linuxefi_boot (void *data)
bf0270
 {
bf0270
+  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
bf0270
+
bf0270
   asm volatile ("cli");
bf0270
 
bf0270
-  return grub_efi_linux_boot ((char *)kernel_mem,
bf0270
-			      handover_offset,
bf0270
-			      params);
bf0270
+  return grub_efi_linux_boot ((char *)context->kernel_mem,
bf0270
+			      context->handover_offset,
bf0270
+			      context->params);
bf0270
 }
bf0270
 
bf0270
 static grub_err_t
bf0270
-grub_linuxefi_unload (void)
bf0270
+grub_linuxefi_unload (void *data)
bf0270
 {
bf0270
+  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
bf0270
+  struct linux_kernel_params *params = context->params;
bf0270
+
bf0270
   grub_dl_unref (my_mod);
bf0270
-  loaded = 0;
bf0270
 
bf0270
-  kernel_free(initrd_mem, params->ramdisk_size);
bf0270
-  kernel_free(linux_cmdline, params->cmdline_size + 1);
bf0270
-  kernel_free(kernel_mem, kernel_size);
bf0270
-  kernel_free(params, sizeof(*params));
bf0270
+  kernel_free (context->initrd_mem, params->ramdisk_size);
bf0270
+  kernel_free (context->cmdline, params->cmdline_size + 1);
bf0270
+  kernel_free (context->kernel_mem, context->kernel_size);
bf0270
+  kernel_free (params, sizeof(*params));
bf0270
+  cmd_initrd->data = 0;
bf0270
+  cmd_initrdefi->data = 0;
bf0270
+  grub_free (context);
bf0270
 
bf0270
   return GRUB_ERR_NONE;
bf0270
 }
bf0270
@@ -188,13 +201,14 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
bf0270
 #define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull))
bf0270
 
bf0270
 static grub_err_t
bf0270
-grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
bf0270
-                 int argc, char *argv[])
bf0270
+grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
bf0270
 {
bf0270
   grub_file_t *files = 0;
bf0270
   int i, nfiles = 0;
bf0270
   grub_size_t size = 0;
bf0270
   grub_uint8_t *ptr;
bf0270
+  struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data;
bf0270
+  struct linux_kernel_params *params;
bf0270
 
bf0270
   if (argc == 0)
bf0270
     {
bf0270
@@ -202,12 +216,14 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
bf0270
       goto fail;
bf0270
     }
bf0270
 
bf0270
-  if (!loaded)
bf0270
+  if (!context)
bf0270
     {
bf0270
       grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
bf0270
       goto fail;
bf0270
     }
bf0270
 
bf0270
+  params = context->params;
bf0270
+
bf0270
   files = grub_calloc (argc, sizeof (files[0]));
bf0270
   if (!files)
bf0270
     goto fail;
bf0270
@@ -226,19 +242,19 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
bf0270
 	}
bf0270
     }
bf0270
 
bf0270
-  initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
bf0270
-  if (initrd_mem == NULL)
bf0270
+  context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
bf0270
+  if (context->initrd_mem == NULL)
bf0270
     goto fail;
bf0270
-  grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
bf0270
+  grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem);
bf0270
 
bf0270
   params->ramdisk_size = LOW_U32(size);
bf0270
-  params->ramdisk_image = LOW_U32(initrd_mem);
bf0270
+  params->ramdisk_image = LOW_U32(context->initrd_mem);
bf0270
 #if defined(__x86_64__)
bf0270
   params->ext_ramdisk_size = HIGH_U32(size);
bf0270
-  params->ext_ramdisk_image = HIGH_U32(initrd_mem);
bf0270
+  params->ext_ramdisk_image = HIGH_U32(context->initrd_mem);
bf0270
 #endif
bf0270
 
bf0270
-  ptr = initrd_mem;
bf0270
+  ptr = context->initrd_mem;
bf0270
 
bf0270
   for (i = 0; i < nfiles; i++)
bf0270
     {
bf0270
@@ -264,8 +280,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
bf0270
     grub_file_close (files[i]);
bf0270
   grub_free (files);
bf0270
 
bf0270
-  if (initrd_mem && grub_errno)
bf0270
-    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem,
bf0270
+  if (context->initrd_mem && grub_errno)
bf0270
+    grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem,
bf0270
 			 BYTES_TO_PAGES(size));
bf0270
 
bf0270
   return grub_errno;
bf0270
@@ -281,6 +297,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
bf0270
   void *kernel = NULL;
bf0270
   int setup_header_end_offset;
bf0270
   int rc;
bf0270
+  void *kernel_mem = 0;
bf0270
+  grub_uint64_t kernel_size = 0;
bf0270
+  grub_uint32_t handover_offset;
bf0270
+  struct linux_kernel_params *params = 0;
bf0270
+  char *cmdline = 0;
bf0270
+  struct grub_linuxefi_context *context = 0;
bf0270
 
bf0270
   grub_dl_ref (my_mod);
bf0270
 
bf0270
@@ -407,27 +429,27 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
bf0270
   grub_dprintf ("linux", "new lh is at %p\n", lh);
bf0270
 
bf0270
   grub_dprintf ("linux", "setting up cmdline\n");
bf0270
-  linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
bf0270
-  if (!linux_cmdline)
bf0270
+  cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
bf0270
+  if (!cmdline)
bf0270
     goto fail;
bf0270
-  grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline);
bf0270
+  grub_dprintf ("linux", "cmdline = %p\n", cmdline);
bf0270
 
bf0270
-  grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
bf0270
+  grub_memcpy (cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
bf0270
   grub_create_loader_cmdline (argc, argv,
bf0270
-                              linux_cmdline + sizeof (LINUX_IMAGE) - 1,
bf0270
+                              cmdline + sizeof (LINUX_IMAGE) - 1,
bf0270
 			      lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1),
bf0270
 			      GRUB_VERIFY_KERNEL_CMDLINE);
bf0270
 
bf0270
-  grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
bf0270
+  grub_dprintf ("linux", "cmdline:%s\n", cmdline);
bf0270
   grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
bf0270
-		LOW_U32(linux_cmdline));
bf0270
-  lh->cmd_line_ptr = LOW_U32(linux_cmdline);
bf0270
+		LOW_U32(cmdline));
bf0270
+  lh->cmd_line_ptr = LOW_U32(cmdline);
bf0270
 #if defined(__x86_64__)
bf0270
-  if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull)
bf0270
+  if ((grub_efi_uintn_t)cmdline > 0xffffffffull)
bf0270
     {
bf0270
       grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n",
bf0270
-		    HIGH_U32(linux_cmdline));
bf0270
-      params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline);
bf0270
+		    HIGH_U32(cmdline));
bf0270
+      params->ext_cmd_line_ptr = HIGH_U32(cmdline);
bf0270
     }
bf0270
 #endif
bf0270
 
bf0270
@@ -452,16 +474,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
bf0270
     }
bf0270
   max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
bf0270
   max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
bf0270
-  kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel"));
bf0270
+  kernel_size = lh->init_size;
bf0270
+  kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel"));
bf0270
   restore_addresses();
bf0270
   if (!kernel_mem)
bf0270
     goto fail;
bf0270
   grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
bf0270
 
bf0270
-  grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
bf0270
-
bf0270
-  loaded = 1;
bf0270
-
bf0270
   grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n",
bf0270
 		LOW_U32(kernel_mem));
bf0270
   lh->code32_start = LOW_U32(kernel_mem);
bf0270
@@ -478,33 +497,42 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
bf0270
 		"setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n",
bf0270
 		params->ext_loader_type, params->ext_loader_ver);
bf0270
 
bf0270
+  context = grub_zalloc (sizeof (*context));
bf0270
+  if (!context)
bf0270
+    goto fail;
bf0270
+  context->kernel_mem = kernel_mem;
bf0270
+  context->kernel_size = kernel_size;
bf0270
+  context->handover_offset = handover_offset;
bf0270
+  context->params = params;
bf0270
+  context->cmdline = cmdline;
bf0270
+
bf0270
+  grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
bf0270
+
bf0270
+  cmd_initrd->data = context;
bf0270
+  cmd_initrdefi->data = context;
bf0270
+
bf0270
+  grub_file_close (file);
bf0270
+  grub_free (kernel);
bf0270
+  return 0;
bf0270
+
bf0270
 fail:
bf0270
   if (file)
bf0270
     grub_file_close (file);
bf0270
 
bf0270
-  if (grub_errno != GRUB_ERR_NONE)
bf0270
-    {
bf0270
-      grub_dl_unref (my_mod);
bf0270
-      loaded = 0;
bf0270
-    }
bf0270
+  grub_dl_unref (my_mod);
bf0270
 
bf0270
-  if (!loaded)
bf0270
-    {
bf0270
-      if (lh)
bf0270
-	kernel_free (linux_cmdline, lh->cmdline_size + 1);
bf0270
+  if (lh)
bf0270
+    kernel_free (cmdline, lh->cmdline_size + 1);
bf0270
 
bf0270
-      kernel_free (kernel_mem, kernel_size);
bf0270
-      kernel_free (params, sizeof(*params));
bf0270
-    }
bf0270
+  kernel_free (kernel_mem, kernel_size);
bf0270
+  kernel_free (params, sizeof(*params));
bf0270
 
bf0270
+  grub_free (context);
bf0270
   grub_free (kernel);
bf0270
 
bf0270
   return grub_errno;
bf0270
 }
bf0270
 
bf0270
-static grub_command_t cmd_linux, cmd_initrd;
bf0270
-static grub_command_t cmd_linuxefi, cmd_initrdefi;
bf0270
-
bf0270
 GRUB_MOD_INIT(linux)
bf0270
 {
bf0270
   cmd_linux =