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

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