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

d18179
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
d18179
From: Chris Coulson <chris.coulson@canonical.com>
d18179
Date: Fri, 29 Apr 2022 21:13:08 +0100
d18179
Subject: [PATCH] loader/efi/chainloader: simplify the loader state
d18179
d18179
When not using the shim lock protocol, the chainloader command retains
d18179
the source buffer and device path passed to LoadImage, requiring the
d18179
unload hook passed to grub_loader_set to free them. It isn't required
d18179
to retain this state though - they aren't required by StartImage or
d18179
anything else in the boot hook, so clean them up before
d18179
grub_cmd_chainloader finishes.
d18179
d18179
This also wraps the loader state when using the shim lock protocol
d18179
inside a struct.
d18179
d18179
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
d18179
(cherry picked from commit fa39862933b3be1553a580a3a5c28073257d8046)
d18179
(cherry picked from commit 0333343ee99c4e88f062789263c94291c057251b)
d18179
[rharwood: verifying twice]
d18179
(cherry picked from commit 6080ad5d91d6a80d5f67c592dd33b6dd413e9453)
d18179
[rharwood: double frees and unintialized, context fuzz - orig_dp]
d18179
(cherry picked from commit b44b88ae45008611ec0469fb47139f4c0d1ee233)
d18179
[rharwood: missing sb support in chainloader]
d18179
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
d18179
---
d18179
 grub-core/loader/efi/chainloader.c | 163 +++++++++++++++++++++++--------------
d18179
 1 file changed, 102 insertions(+), 61 deletions(-)
d18179
d18179
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
d18179
index eeef52acde..8d7072ee1a 100644
d18179
--- a/grub-core/loader/efi/chainloader.c
d18179
+++ b/grub-core/loader/efi/chainloader.c
d18179
@@ -47,38 +47,21 @@ GRUB_MOD_LICENSE ("GPLv3+");
d18179
 
d18179
 static grub_dl_t my_mod;
d18179
 
d18179
-static grub_efi_physical_address_t address;
d18179
-static grub_efi_uintn_t pages;
d18179
-static grub_ssize_t fsize;
d18179
-static grub_efi_device_path_t *file_path;
d18179
 static grub_efi_handle_t image_handle;
d18179
-static grub_efi_char16_t *cmdline;
d18179
-static grub_ssize_t cmdline_len;
d18179
-static grub_efi_handle_t dev_handle;
d18179
 
d18179
-static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
d18179
+struct grub_secureboot_chainloader_context {
d18179
+  grub_efi_physical_address_t address;
d18179
+  grub_efi_uintn_t pages;
d18179
+  grub_ssize_t fsize;
d18179
+  grub_efi_device_path_t *file_path;
d18179
+  grub_efi_char16_t *cmdline;
d18179
+  grub_ssize_t cmdline_len;
d18179
+  grub_efi_handle_t dev_handle;
d18179
+};
d18179
+static struct grub_secureboot_chainloader_context *sb_context;
d18179
 
d18179
 static grub_err_t
d18179
-grub_chainloader_unload (void)
d18179
-{
d18179
-  grub_efi_boot_services_t *b;
d18179
-
d18179
-  b = grub_efi_system_table->boot_services;
d18179
-  efi_call_1 (b->unload_image, image_handle);
d18179
-  efi_call_2 (b->free_pages, address, pages);
d18179
-
d18179
-  grub_free (file_path);
d18179
-  grub_free (cmdline);
d18179
-  cmdline = 0;
d18179
-  file_path = 0;
d18179
-  dev_handle = 0;
d18179
-
d18179
-  grub_dl_unref (my_mod);
d18179
-  return GRUB_ERR_NONE;
d18179
-}
d18179
-
d18179
-static grub_err_t
d18179
-grub_chainloader_boot (void)
d18179
+grub_start_image (grub_efi_handle_t handle)
d18179
 {
d18179
   grub_efi_boot_services_t *b;
d18179
   grub_efi_status_t status;
d18179
@@ -86,7 +69,7 @@ grub_chainloader_boot (void)
d18179
   grub_efi_char16_t *exit_data = NULL;
d18179
 
d18179
   b = grub_efi_system_table->boot_services;
d18179
-  status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
d18179
+  status = efi_call_3 (b->start_image, handle, &exit_data_size, &exit_data);
d18179
   if (status != GRUB_EFI_SUCCESS)
d18179
     {
d18179
       if (exit_data)
d18179
@@ -110,11 +93,37 @@ grub_chainloader_boot (void)
d18179
   if (exit_data)
d18179
     efi_call_1 (b->free_pool, exit_data);
d18179
 
d18179
-  grub_loader_unset ();
d18179
-
d18179
   return grub_errno;
d18179
 }
d18179
 
d18179
+static grub_err_t
d18179
+grub_chainloader_unload (void)
d18179
+{
d18179
+  grub_efi_loaded_image_t *loaded_image;
d18179
+  grub_efi_boot_services_t *b;
d18179
+
d18179
+  loaded_image = grub_efi_get_loaded_image (image_handle);
d18179
+  if (loaded_image != NULL)
d18179
+    grub_free (loaded_image->load_options);
d18179
+
d18179
+  b = grub_efi_system_table->boot_services;
d18179
+  efi_call_1 (b->unload_image, image_handle);
d18179
+
d18179
+  grub_dl_unref (my_mod);
d18179
+  return GRUB_ERR_NONE;
d18179
+}
d18179
+
d18179
+static grub_err_t
d18179
+grub_chainloader_boot (void)
d18179
+{
d18179
+  grub_err_t err;
d18179
+
d18179
+  err = grub_start_image (image_handle);
d18179
+
d18179
+  grub_loader_unset ();
d18179
+  return err;
d18179
+}
d18179
+
d18179
 static grub_err_t
d18179
 copy_file_path (grub_efi_file_path_device_path_t *fp,
d18179
 		const char *str, grub_efi_uint16_t len)
d18179
@@ -149,7 +158,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
d18179
   char *dir_start;
d18179
   char *dir_end;
d18179
   grub_size_t size;
d18179
-  grub_efi_device_path_t *d;
d18179
+  grub_efi_device_path_t *d, *file_path;
d18179
 
d18179
   dir_start = grub_strchr (filename, ')');
d18179
   if (! dir_start)
d18179
@@ -520,12 +529,14 @@ grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
d18179
 }
d18179
 
d18179
 static grub_efi_boolean_t
d18179
-handle_image (void *data, grub_efi_uint32_t datasize)
d18179
+handle_image (struct grub_secureboot_chainloader_context *load_context)
d18179
 {
d18179
   grub_efi_boot_services_t *b;
d18179
   grub_efi_loaded_image_t *li, li_bak;
d18179
   grub_efi_status_t efi_status;
d18179
-  char *buffer = NULL;
d18179
+  void *data = (void *)(unsigned long)load_context->address;
d18179
+  grub_efi_uint32_t datasize = load_context->fsize;
d18179
+  void *buffer = NULL;
d18179
   char *buffer_aligned = NULL;
d18179
   grub_efi_uint32_t i;
d18179
   struct grub_pe32_section_table *section;
d18179
@@ -535,6 +546,7 @@ handle_image (void *data, grub_efi_uint32_t datasize)
d18179
   grub_uint32_t buffer_size;
d18179
   int found_entry_point = 0;
d18179
   int rc;
d18179
+  grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
d18179
 
d18179
   b = grub_efi_system_table->boot_services;
d18179
 
d18179
@@ -794,10 +806,10 @@ handle_image (void *data, grub_efi_uint32_t datasize)
d18179
   grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t));
d18179
   li->image_base = buffer_aligned;
d18179
   li->image_size = context.image_size;
d18179
-  li->load_options = cmdline;
d18179
-  li->load_options_size = cmdline_len;
d18179
-  li->file_path = grub_efi_get_media_file_path (file_path);
d18179
-  li->device_handle = dev_handle;
d18179
+  li->load_options = load_context->cmdline;
d18179
+  li->load_options_size = load_context->cmdline_len;
d18179
+  li->file_path = grub_efi_get_media_file_path (load_context->file_path);
d18179
+  li->device_handle = load_context->dev_handle;
d18179
   if (!li->file_path)
d18179
     {
d18179
       grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
d18179
@@ -825,22 +837,22 @@ error_exit:
d18179
 static grub_err_t
d18179
 grub_secureboot_chainloader_unload (void)
d18179
 {
d18179
-  grub_efi_boot_services_t *b;
d18179
+  grub_efi_free_pages (sb_context->address, sb_context->pages);
d18179
+  grub_free (sb_context->file_path);
d18179
+  grub_free (sb_context->cmdline);
d18179
+  grub_free (sb_context);
d18179
 
d18179
-  b = grub_efi_system_table->boot_services;
d18179
-  efi_call_2 (b->free_pages, address, pages);
d18179
-  grub_free (file_path);
d18179
-  grub_free (cmdline);
d18179
-  cmdline = 0;
d18179
-  file_path = 0;
d18179
-  dev_handle = 0;
d18179
+  sb_context = 0;
d18179
 
d18179
   grub_dl_unref (my_mod);
d18179
   return GRUB_ERR_NONE;
d18179
 }
d18179
 
d18179
 static grub_err_t
d18179
-grub_load_image(void *boot_image)
d18179
+grub_load_image(grub_efi_device_path_t *file_path, void *boot_image,
d18179
+		grub_efi_uintn_t image_size, grub_efi_handle_t dev_handle,
d18179
+		grub_efi_char16_t *cmdline, grub_ssize_t cmdline_len,
d18179
+		grub_efi_handle_t *image_handle_out)
d18179
 {
d18179
   grub_efi_boot_services_t *b;
d18179
   grub_efi_status_t status;
d18179
@@ -849,7 +861,7 @@ grub_load_image(void *boot_image)
d18179
   b = grub_efi_system_table->boot_services;
d18179
 
d18179
   status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
d18179
-		       boot_image, fsize, &image_handle);
d18179
+		       boot_image, image_size, image_handle_out);
d18179
   if (status != GRUB_EFI_SUCCESS)
d18179
     {
d18179
       if (status == GRUB_EFI_OUT_OF_RESOURCES)
d18179
@@ -862,7 +874,7 @@ grub_load_image(void *boot_image)
d18179
   /* LoadImage does not set a device handler when the image is
d18179
      loaded from memory, so it is necessary to set it explicitly here.
d18179
      This is a mess.  */
d18179
-  loaded_image = grub_efi_get_loaded_image (image_handle);
d18179
+  loaded_image = grub_efi_get_loaded_image (*image_handle_out);
d18179
   if (! loaded_image)
d18179
     {
d18179
       grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
d18179
@@ -884,20 +896,25 @@ grub_secureboot_chainloader_boot (void)
d18179
 {
d18179
   grub_efi_boot_services_t *b;
d18179
   int rc;
d18179
+  grub_efi_handle_t handle = 0;
d18179
 
d18179
-  rc = handle_image ((void *)(unsigned long)address, fsize);
d18179
+  rc = handle_image (sb_context);
d18179
   if (rc == 0)
d18179
     {
d18179
       /* We weren't able to attempt to execute the image, so fall back
d18179
        * to LoadImage / StartImage.
d18179
        */
d18179
-      rc = grub_load_image((void *)(unsigned long)address);
d18179
+      rc = grub_load_image(sb_context->file_path,
d18179
+			   (void *)(unsigned long)sb_context->address,
d18179
+			   sb_context->fsize, sb_context->dev_handle,
d18179
+			   sb_context->cmdline, sb_context->cmdline_len,
d18179
+			   &handle);
d18179
       if (rc == 0)
d18179
-        grub_chainloader_boot ();
d18179
+	grub_start_image (handle);
d18179
     }
d18179
 
d18179
   b = grub_efi_system_table->boot_services;
d18179
-  efi_call_1 (b->unload_image, image_handle);
d18179
+  efi_call_1 (b->unload_image, handle);
d18179
 
d18179
   grub_loader_unset ();
d18179
   return grub_errno;
d18179
@@ -911,10 +928,16 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
d18179
   grub_efi_status_t status;
d18179
   grub_efi_boot_services_t *b;
d18179
   grub_device_t dev = 0;
d18179
-  grub_efi_device_path_t *dp = 0;
d18179
+  grub_efi_device_path_t *dp = 0, *file_path = 0;
d18179
   char *filename;
d18179
   void *boot_image = 0;
d18179
   int rc;
d18179
+  grub_efi_physical_address_t address = 0;
d18179
+  grub_ssize_t fsize;
d18179
+  grub_efi_uintn_t pages = 0;
d18179
+  grub_efi_char16_t *cmdline = 0;
d18179
+  grub_ssize_t cmdline_len = 0;
d18179
+  grub_efi_handle_t dev_handle = 0;
d18179
 
d18179
   if (argc == 0)
d18179
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
d18179
@@ -922,12 +945,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
d18179
 
d18179
   grub_dl_ref (my_mod);
d18179
 
d18179
-  /* Initialize some global variables.  */
d18179
-  address = 0;
d18179
-  image_handle = 0;
d18179
-  file_path = 0;
d18179
-  dev_handle = 0;
d18179
-
d18179
   b = grub_efi_system_table->boot_services;
d18179
 
d18179
   if (argc > 1)
d18179
@@ -1079,15 +1096,33 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
d18179
   grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc);
d18179
   if (rc > 0)
d18179
     {
d18179
+      sb_context = grub_malloc (sizeof (*sb_context));
d18179
+      if (sb_context == NULL)
d18179
+        goto fail;
d18179
+      sb_context->address = address;
d18179
+      sb_context->fsize = fsize;
d18179
+      sb_context->pages = pages;
d18179
+      sb_context->file_path = file_path;
d18179
+      sb_context->cmdline = cmdline;
d18179
+      sb_context->cmdline_len = cmdline_len;
d18179
+      sb_context->dev_handle = dev_handle;
d18179
+
d18179
       grub_file_close (file);
d18179
+
d18179
       grub_loader_set (grub_secureboot_chainloader_boot,
d18179
 		       grub_secureboot_chainloader_unload, 0);
d18179
       return 0;
d18179
     }
d18179
   else if (rc == 0)
d18179
     {
d18179
-      grub_load_image(boot_image);
d18179
+      grub_load_image(file_path, boot_image, fsize, dev_handle, cmdline,
d18179
+		      cmdline_len, &image_handle);
d18179
       grub_file_close (file);
d18179
+
d18179
+      /* We're finished with the source image buffer and file path now */
d18179
+      efi_call_2 (b->free_pages, address, pages);
d18179
+      grub_free (file_path);
d18179
+
d18179
       grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
d18179
 
d18179
       return 0;
d18179
@@ -1109,6 +1144,12 @@ fail:
d18179
   if (cmdline)
d18179
     grub_free (cmdline);
d18179
 
d18179
+  if (image_handle != 0)
d18179
+    {
d18179
+      efi_call_1 (b->unload_image, image_handle);
d18179
+      image_handle = 0;
d18179
+    }
d18179
+
d18179
   grub_dl_unref (my_mod);
d18179
 
d18179
   return grub_errno;