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

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