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

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