efi/linux: set the loaded image device path when it is missing
authorMate Kukri <mate.kukri@canonical.com>
Tue, 23 Jun 2026 14:19:15 +0000 (15:19 +0100)
committerMate Kukri <mate.kukri@canonical.com>
Tue, 23 Jun 2026 15:04:12 +0000 (16:04 +0100)
Under Secure Boot grub verifies the kernel in its verifier framework
(grub-core/kern/efi/sb.c). The verifier loads it through the shim image loader
protocol, passing only the image buffer and a NULL device path, because the
device path is not available at that point. The verified image handle, which
linux reuses, is therefore left with no device path: both
loaded_image->file_path and the LOADED_IMAGE_DEVICE_PATH protocol are NULL.

This is a consequence of grub loading the image inside the verifier, separate
from where the device path is built. The kernel should see the same memory
mapped path it would get from a normal load.

A memory mapped device path is already built for grub_efi_load_image(). Also
point loaded_image->file_path and the LOADED_IMAGE_DEVICE_PATH protocol at it.
The image's loader owns those fields and frees them with the EFI pool
allocator, so save the originals and restore them before unloading the image if
it returns.

Signed-off-by: Mate Kukri <mate.kukri@canonical.com>
Gbp-Pq: Topic upstream
Gbp-Pq: Name efi-linux-set-loaded-image-device-path.patch

grub-core/loader/efi/linux.c

index adf8b9ee915ce174d5fa63c1112e947e79e57870..c53f73f2769bb4c499a7e434e86797704ccd17d4 100644 (file)
@@ -54,6 +54,10 @@ static bool initrd_use_loadfile2 = false;
 
 static grub_guid_t load_file2_guid = GRUB_EFI_LOAD_FILE2_PROTOCOL_GUID;
 static grub_guid_t device_path_guid = GRUB_EFI_DEVICE_PATH_GUID;
+/* EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID. */
+static grub_guid_t loaded_image_device_path_guid =
+  { 0xbc62157e, 0x3e33, 0x4fec,
+    { 0x99, 0x20, 0x2d, 0x3b, 0x36, 0xd7, 0x50, 0xdf } };
 
 /*
  * Clang will produce a warning for missing initializer for the
@@ -197,6 +201,10 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
   grub_efi_handle_t image_handle;
   grub_efi_status_t status;
   grub_efi_loaded_image_t *loaded_image;
+  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+  grub_efi_device_path_t *saved_file_path = NULL;
+  grub_efi_device_path_t *saved_device_path = NULL;
+  bool override_dp = false;
   grub_size_t len;
   grub_size_t args_len;
 
@@ -228,23 +236,47 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
        }
     }
 
-  grub_free (mempath);
-
   grub_dprintf ("linux", "linux command line: '%s'\n", args);
 
   /* Convert command line to UTF-16. */
   loaded_image = grub_efi_get_loaded_image (image_handle);
   if (loaded_image == NULL)
     {
+      grub_free (mempath);
       grub_error (GRUB_ERR_BAD_FIRMWARE, "missing loaded_image proto");
       goto unload;
     }
+
+  /*
+   * The verifier may have loaded this image with no device path (see
+   * grub-core/kern/efi/sb.c): both loaded_image->file_path and the
+   * LOADED_IMAGE_DEVICE_PATH protocol are left NULL. Point them at the memory
+   * mapped path we built, matching what grub_efi_load_image() sets, and
+   * remember the originals so they can be restored if the image returns and has
+   * to be unloaded.
+   */
+  if (loaded_image->file_path == NULL)
+    {
+      saved_file_path = loaded_image->file_path;
+      loaded_image->file_path = (grub_efi_device_path_t *) mempath;
+
+      b->handle_protocol (image_handle, &loaded_image_device_path_guid,
+                         (void **) &saved_device_path);
+      b->reinstall_protocol_interface (image_handle,
+                                      &loaded_image_device_path_guid,
+                                      saved_device_path,
+                                      (grub_efi_device_path_t *) mempath);
+      override_dp = true;
+    }
+  else
+    grub_free (mempath);  /* the image's loader made its own copy */
+
   args_len = grub_strlen (args);
   len = (args_len + 1) * sizeof (grub_efi_char16_t);
   loaded_image->load_options =
     grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (len));
   if (!loaded_image->load_options)
-    return grub_errno;
+    goto unload;
 
   len = grub_utf8_to_utf16 (loaded_image->load_options, len,
                            (grub_uint8_t *) args, args_len, NULL);
@@ -261,6 +293,16 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
                       GRUB_EFI_BYTES_TO_PAGES (len));
   loaded_image->load_options = NULL;
 unload:
+  /* Restore the device path so the image's loader frees its own copy. */
+  if (override_dp)
+    {
+      loaded_image->file_path = saved_file_path;
+      b->reinstall_protocol_interface (image_handle,
+                                      &loaded_image_device_path_guid,
+                                      (grub_efi_device_path_t *) mempath,
+                                      saved_device_path);
+      grub_free (mempath);
+    }
   grub_efi_unload_image (image_handle);
 
   return grub_errno;