Blame SOURCES/0233-loader-efi-chainloader-simplify-the-loader-state.patch

1c6ba0
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
1c6ba0
From: Chris Coulson <chris.coulson@canonical.com>
1c6ba0
Date: Fri, 29 Apr 2022 21:13:08 +0100
1c6ba0
Subject: [PATCH] loader/efi/chainloader: simplify the loader state
1c6ba0
1c6ba0
When not using the shim lock protocol, the chainloader command retains
1c6ba0
the source buffer and device path passed to LoadImage, requiring the
1c6ba0
unload hook passed to grub_loader_set to free them. It isn't required
1c6ba0
to retain this state though - they aren't required by StartImage or
1c6ba0
anything else in the boot hook, so clean them up before
1c6ba0
grub_cmd_chainloader finishes.
1c6ba0
1c6ba0
This also wraps the loader state when using the shim lock protocol
1c6ba0
inside a struct.
1c6ba0
1c6ba0
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
1c6ba0
(cherry picked from commit fa39862933b3be1553a580a3a5c28073257d8046)
1c6ba0
(cherry picked from commit 0333343ee99c4e88f062789263c94291c057251b)
1c6ba0
[rharwood: double-frees and uninitialized, verifying twice]
1c6ba0
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
1c6ba0
---
1c6ba0
 grub-core/loader/efi/chainloader.c | 160 +++++++++++++++++++++++--------------
1c6ba0
 1 file changed, 102 insertions(+), 58 deletions(-)
1c6ba0
1c6ba0
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
1c6ba0
index 39158e679e..0717ce0478 100644
1c6ba0
--- a/grub-core/loader/efi/chainloader.c
1c6ba0
+++ b/grub-core/loader/efi/chainloader.c
1c6ba0
@@ -48,38 +48,21 @@ GRUB_MOD_LICENSE ("GPLv3+");
1c6ba0
 
1c6ba0
 static grub_dl_t my_mod;
1c6ba0
 
1c6ba0
-static grub_efi_physical_address_t address;
1c6ba0
-static grub_efi_uintn_t pages;
1c6ba0
-static grub_ssize_t fsize;
1c6ba0
-static grub_efi_device_path_t *file_path;
1c6ba0
 static grub_efi_handle_t image_handle;
1c6ba0
-static grub_efi_char16_t *cmdline;
1c6ba0
-static grub_ssize_t cmdline_len;
1c6ba0
-static grub_efi_handle_t dev_handle;
1c6ba0
 
1c6ba0
-static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
1c6ba0
+struct grub_secureboot_chainloader_context {
1c6ba0
+  grub_efi_physical_address_t address;
1c6ba0
+  grub_efi_uintn_t pages;
1c6ba0
+  grub_ssize_t fsize;
1c6ba0
+  grub_efi_device_path_t *file_path;
1c6ba0
+  grub_efi_char16_t *cmdline;
1c6ba0
+  grub_ssize_t cmdline_len;
1c6ba0
+  grub_efi_handle_t dev_handle;
1c6ba0
+};
1c6ba0
+static struct grub_secureboot_chainloader_context *sb_context;
1c6ba0
 
1c6ba0
 static grub_err_t
1c6ba0
-grub_chainloader_unload (void)
1c6ba0
-{
1c6ba0
-  grub_efi_boot_services_t *b;
1c6ba0
-
1c6ba0
-  b = grub_efi_system_table->boot_services;
1c6ba0
-  efi_call_1 (b->unload_image, image_handle);
1c6ba0
-  grub_efi_free_pages (address, pages);
1c6ba0
-
1c6ba0
-  grub_free (file_path);
1c6ba0
-  grub_free (cmdline);
1c6ba0
-  cmdline = 0;
1c6ba0
-  file_path = 0;
1c6ba0
-  dev_handle = 0;
1c6ba0
-
1c6ba0
-  grub_dl_unref (my_mod);
1c6ba0
-  return GRUB_ERR_NONE;
1c6ba0
-}
1c6ba0
-
1c6ba0
-static grub_err_t
1c6ba0
-grub_chainloader_boot (void)
1c6ba0
+grub_start_image (grub_efi_handle_t handle)
1c6ba0
 {
1c6ba0
   grub_efi_boot_services_t *b;
1c6ba0
   grub_efi_status_t status;
1c6ba0
@@ -87,7 +70,7 @@ grub_chainloader_boot (void)
1c6ba0
   grub_efi_char16_t *exit_data = NULL;
1c6ba0
 
1c6ba0
   b = grub_efi_system_table->boot_services;
1c6ba0
-  status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
1c6ba0
+  status = efi_call_3 (b->start_image, handle, &exit_data_size, &exit_data);
1c6ba0
   if (status != GRUB_EFI_SUCCESS)
1c6ba0
     {
1c6ba0
       if (exit_data)
1c6ba0
@@ -111,11 +94,37 @@ grub_chainloader_boot (void)
1c6ba0
   if (exit_data)
1c6ba0
     grub_efi_free_pool (exit_data);
1c6ba0
 
1c6ba0
-  grub_loader_unset ();
1c6ba0
-
1c6ba0
   return grub_errno;
1c6ba0
 }
1c6ba0
 
1c6ba0
+static grub_err_t
1c6ba0
+grub_chainloader_unload (void)
1c6ba0
+{
1c6ba0
+  grub_efi_loaded_image_t *loaded_image;
1c6ba0
+  grub_efi_boot_services_t *b;
1c6ba0
+
1c6ba0
+  loaded_image = grub_efi_get_loaded_image (image_handle);
1c6ba0
+  if (loaded_image != NULL)
1c6ba0
+    grub_free (loaded_image->load_options);
1c6ba0
+
1c6ba0
+  b = grub_efi_system_table->boot_services;
1c6ba0
+  efi_call_1 (b->unload_image, image_handle);
1c6ba0
+
1c6ba0
+  grub_dl_unref (my_mod);
1c6ba0
+  return GRUB_ERR_NONE;
1c6ba0
+}
1c6ba0
+
1c6ba0
+static grub_err_t
1c6ba0
+grub_chainloader_boot (void)
1c6ba0
+{
1c6ba0
+  grub_err_t err;
1c6ba0
+
1c6ba0
+  err = grub_start_image (image_handle);
1c6ba0
+
1c6ba0
+  grub_loader_unset ();
1c6ba0
+  return err;
1c6ba0
+}
1c6ba0
+
1c6ba0
 static grub_err_t
1c6ba0
 copy_file_path (grub_efi_file_path_device_path_t *fp,
1c6ba0
 		const char *str, grub_efi_uint16_t len)
1c6ba0
@@ -150,7 +159,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
1c6ba0
   char *dir_start;
1c6ba0
   char *dir_end;
1c6ba0
   grub_size_t size;
1c6ba0
-  grub_efi_device_path_t *d;
1c6ba0
+  grub_efi_device_path_t *d, *file_path;
1c6ba0
 
1c6ba0
   dir_start = grub_strchr (filename, ')');
1c6ba0
   if (! dir_start)
1c6ba0
@@ -526,10 +535,12 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
1c6ba0
 }
1c6ba0
 
1c6ba0
 static grub_efi_boolean_t
1c6ba0
-handle_image (void *data, grub_efi_uint32_t datasize)
1c6ba0
+handle_image (struct grub_secureboot_chainloader_context *load_context)
1c6ba0
 {
1c6ba0
   grub_efi_loaded_image_t *li, li_bak;
1c6ba0
   grub_efi_status_t efi_status;
1c6ba0
+  void *data = (void *)(unsigned long)load_context->address;
1c6ba0
+  grub_efi_uint32_t datasize = load_context->fsize;
1c6ba0
   void *buffer = NULL;
1c6ba0
   char *buffer_aligned = NULL;
1c6ba0
   grub_efi_uint32_t i;
1c6ba0
@@ -540,6 +551,7 @@ handle_image (void *data, grub_efi_uint32_t datasize)
1c6ba0
   grub_uint32_t buffer_size;
1c6ba0
   int found_entry_point = 0;
1c6ba0
   int rc;
1c6ba0
+  grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
1c6ba0
 
1c6ba0
   rc = read_header (data, datasize, &context);
1c6ba0
   if (rc < 0)
1c6ba0
@@ -797,10 +809,10 @@ handle_image (void *data, grub_efi_uint32_t datasize)
1c6ba0
   grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t));
1c6ba0
   li->image_base = buffer_aligned;
1c6ba0
   li->image_size = context.image_size;
1c6ba0
-  li->load_options = cmdline;
1c6ba0
-  li->load_options_size = cmdline_len;
1c6ba0
-  li->file_path = grub_efi_get_media_file_path (file_path);
1c6ba0
-  li->device_handle = dev_handle;
1c6ba0
+  li->load_options = load_context->cmdline;
1c6ba0
+  li->load_options_size = load_context->cmdline_len;
1c6ba0
+  li->file_path = grub_efi_get_media_file_path (load_context->file_path);
1c6ba0
+  li->device_handle = load_context->dev_handle;
1c6ba0
   if (!li->file_path)
1c6ba0
     {
1c6ba0
       grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
1c6ba0
@@ -829,19 +841,22 @@ error_exit:
1c6ba0
 static grub_err_t
1c6ba0
 grub_secureboot_chainloader_unload (void)
1c6ba0
 {
1c6ba0
-  grub_efi_free_pages (address, pages);
1c6ba0
-  grub_free (file_path);
1c6ba0
-  grub_free (cmdline);
1c6ba0
-  cmdline = 0;
1c6ba0
-  file_path = 0;
1c6ba0
-  dev_handle = 0;
1c6ba0
+  grub_efi_free_pages (sb_context->address, sb_context->pages);
1c6ba0
+  grub_free (sb_context->file_path);
1c6ba0
+  grub_free (sb_context->cmdline);
1c6ba0
+  grub_free (sb_context);
1c6ba0
+
1c6ba0
+  sb_context = 0;
1c6ba0
 
1c6ba0
   grub_dl_unref (my_mod);
1c6ba0
   return GRUB_ERR_NONE;
1c6ba0
 }
1c6ba0
 
1c6ba0
 static grub_err_t
1c6ba0
-grub_load_image(void *boot_image)
1c6ba0
+grub_load_image(grub_efi_device_path_t *file_path, void *boot_image,
1c6ba0
+		grub_efi_uintn_t image_size, grub_efi_handle_t dev_handle,
1c6ba0
+		grub_efi_char16_t *cmdline, grub_ssize_t cmdline_len,
1c6ba0
+		grub_efi_handle_t *image_handle_out)
1c6ba0
 {
1c6ba0
   grub_efi_boot_services_t *b;
1c6ba0
   grub_efi_status_t status;
1c6ba0
@@ -850,7 +865,7 @@ grub_load_image(void *boot_image)
1c6ba0
   b = grub_efi_system_table->boot_services;
1c6ba0
 
1c6ba0
   status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
1c6ba0
-		       boot_image, fsize, &image_handle);
1c6ba0
+		       boot_image, image_size, image_handle_out);
1c6ba0
   if (status != GRUB_EFI_SUCCESS)
1c6ba0
     {
1c6ba0
       if (status == GRUB_EFI_OUT_OF_RESOURCES)
1c6ba0
@@ -863,7 +878,7 @@ grub_load_image(void *boot_image)
1c6ba0
   /* LoadImage does not set a device handler when the image is
1c6ba0
      loaded from memory, so it is necessary to set it explicitly here.
1c6ba0
      This is a mess.  */
1c6ba0
-  loaded_image = grub_efi_get_loaded_image (image_handle);
1c6ba0
+  loaded_image = grub_efi_get_loaded_image (*image_handle_out);
1c6ba0
   if (! loaded_image)
1c6ba0
     {
1c6ba0
       grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
1c6ba0
@@ -885,20 +900,25 @@ grub_secureboot_chainloader_boot (void)
1c6ba0
 {
1c6ba0
   grub_efi_boot_services_t *b;
1c6ba0
   int rc;
1c6ba0
+  grub_efi_handle_t handle = 0;
1c6ba0
 
1c6ba0
-  rc = handle_image ((void *)(unsigned long)address, fsize);
1c6ba0
+  rc = handle_image (sb_context);
1c6ba0
   if (rc == 0)
1c6ba0
     {
1c6ba0
       /* We weren't able to attempt to execute the image, so fall back
1c6ba0
        * to LoadImage / StartImage.
1c6ba0
        */
1c6ba0
-      rc = grub_load_image((void *)(unsigned long)address);
1c6ba0
+      rc = grub_load_image(sb_context->file_path,
1c6ba0
+			   (void *)(unsigned long)sb_context->address,
1c6ba0
+			   sb_context->fsize, sb_context->dev_handle,
1c6ba0
+			   sb_context->cmdline, sb_context->cmdline_len,
1c6ba0
+			   &handle);
1c6ba0
       if (rc == 0)
1c6ba0
-        grub_chainloader_boot ();
1c6ba0
+	grub_start_image (handle);
1c6ba0
     }
1c6ba0
 
1c6ba0
   b = grub_efi_system_table->boot_services;
1c6ba0
-  efi_call_1 (b->unload_image, image_handle);
1c6ba0
+  efi_call_1 (b->unload_image, handle);
1c6ba0
 
1c6ba0
   grub_loader_unset ();
1c6ba0
   return grub_errno;
1c6ba0
@@ -913,10 +933,16 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   grub_efi_boot_services_t *b;
1c6ba0
   grub_device_t dev = 0;
1c6ba0
   grub_device_t orig_dev = 0;
1c6ba0
-  grub_efi_device_path_t *dp = 0;
1c6ba0
+  grub_efi_device_path_t *dp = 0, *file_path = 0;
1c6ba0
   char *filename;
1c6ba0
   void *boot_image = 0;
1c6ba0
   int rc;
1c6ba0
+  grub_efi_physical_address_t address = 0;
1c6ba0
+  grub_ssize_t fsize;
1c6ba0
+  grub_efi_uintn_t pages = 0;
1c6ba0
+  grub_efi_char16_t *cmdline = 0;
1c6ba0
+  grub_ssize_t cmdline_len = 0;
1c6ba0
+  grub_efi_handle_t dev_handle = 0;
1c6ba0
 
1c6ba0
   if (argc == 0)
1c6ba0
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
1c6ba0
@@ -924,12 +950,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
 
1c6ba0
   grub_dl_ref (my_mod);
1c6ba0
 
1c6ba0
-  /* Initialize some global variables.  */
1c6ba0
-  address = 0;
1c6ba0
-  image_handle = 0;
1c6ba0
-  file_path = 0;
1c6ba0
-  dev_handle = 0;
1c6ba0
-
1c6ba0
   b = grub_efi_system_table->boot_services;
1c6ba0
 
1c6ba0
   if (argc > 1)
1c6ba0
@@ -1096,17 +1116,35 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
1c6ba0
   grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc);
1c6ba0
   if (rc > 0)
1c6ba0
     {
1c6ba0
+      sb_context = grub_malloc (sizeof (*sb_context));
1c6ba0
+      if (sb_context == NULL)
1c6ba0
+        goto fail;
1c6ba0
+      sb_context->address = address;
1c6ba0
+      sb_context->fsize = fsize;
1c6ba0
+      sb_context->pages = pages;
1c6ba0
+      sb_context->file_path = file_path;
1c6ba0
+      sb_context->cmdline = cmdline;
1c6ba0
+      sb_context->cmdline_len = cmdline_len;
1c6ba0
+      sb_context->dev_handle = dev_handle;
1c6ba0
+
1c6ba0
       grub_file_close (file);
1c6ba0
       grub_device_close (dev);
1c6ba0
+
1c6ba0
       grub_loader_set (grub_secureboot_chainloader_boot,
1c6ba0
 		       grub_secureboot_chainloader_unload, 0);
1c6ba0
       return 0;
1c6ba0
     }
1c6ba0
   else if (rc == 0)
1c6ba0
     {
1c6ba0
-      grub_load_image(boot_image);
1c6ba0
+      grub_load_image(file_path, boot_image, fsize, dev_handle, cmdline,
1c6ba0
+		      cmdline_len, &image_handle);
1c6ba0
       grub_file_close (file);
1c6ba0
       grub_device_close (dev);
1c6ba0
+
1c6ba0
+      /* We're finished with the source image buffer and file path now */
1c6ba0
+      efi_call_2 (b->free_pages, address, pages);
1c6ba0
+      grub_free (file_path);
1c6ba0
+
1c6ba0
       grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
1c6ba0
 
1c6ba0
       return 0;
1c6ba0
@@ -1134,6 +1172,12 @@ fail:
1c6ba0
   if (cmdline)
1c6ba0
     grub_free (cmdline);
1c6ba0
 
1c6ba0
+  if (image_handle != 0)
1c6ba0
+    {
1c6ba0
+      efi_call_1 (b->unload_image, image_handle);
1c6ba0
+      image_handle = 0;
1c6ba0
+    }
1c6ba0
+
1c6ba0
   grub_dl_unref (my_mod);
1c6ba0
 
1c6ba0
   return grub_errno;