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

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