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

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