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

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