stub: fix file path handling for loaded kernel

- Actually pass the new memory file path to parent_loaded_image->FilePath
- Restore old parent_loaded_image if Linux returns
- Pass the same kernel_file_path in load_via_boot_services path
- s/Re-use/Patch in comment explaining what we are doing

Fixes #38566
This commit is contained in:
Tobias Heider
2025-08-25 16:07:54 +02:00
committed by Yu Watanabe
parent f871c20bad
commit f405165065

View File

@@ -20,9 +20,6 @@
#include "shim.h"
#include "util.h"
#define STUB_PAYLOAD_GUID \
{ 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
typedef struct {
MEMMAP_DEVICE_PATH memmap_path;
EFI_DEVICE_PATH end_path;
@@ -56,22 +53,12 @@ static EFI_STATUS load_via_boot_services(
uint32_t compat_entry_point,
const char16_t *cmdline,
const struct iovec *kernel,
const struct iovec *initrd) {
const struct iovec *initrd,
KERNEL_FILE_PATH *kernel_file_path) {
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
EFI_LOADED_IMAGE_PROTOCOL* loaded_image = NULL;
EFI_STATUS err;
VENDOR_DEVICE_PATH device_node = {
.Header = {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_VENDOR_DP,
.Length = sizeof(device_node),
},
.Guid = STUB_PAYLOAD_GUID,
};
_cleanup_free_ EFI_DEVICE_PATH* file_path = device_path_replace_node(parent_loaded_image->FilePath, NULL, &device_node.Header);
/* When running with shim < v16 and booting a UKI directly from it, without a second stage loader,
* the shim verify protocol needs to be called or it will raise a security violation when starting
* the image (e.g.: Fedora Cloud Base UKI). TODO: drop once support for shim < v16 is not needed. */
@@ -81,13 +68,13 @@ static EFI_STATUS load_via_boot_services(
&(ValidationContext) {
.addr = kernel->iov_base,
.len = kernel->iov_len,
.device_path = file_path,
.device_path = &kernel_file_path->memmap_path.Header,
});
err = BS->LoadImage(/* BootPolicy= */false,
parent,
file_path,
&kernel_file_path->memmap_path.Header,
kernel->iov_base,
kernel->iov_len,
&kernel_image);
@@ -204,17 +191,27 @@ EFI_STATUS linux_exec(
if (err != EFI_SUCCESS)
return log_error_status(err, "Bad kernel image: %m");
/* Re-use the parent_image(_handle) and parent_loaded_image for the kernel image we are about to execute.
* We have to do this, because if kernel stub code passes its own handle to certain firmware functions,
* the firmware could cast EFI_LOADED_IMAGE_PROTOCOL * to a larger struct to access its own private data,
* and if we allocated a smaller struct, that could cause problems.
* This is modeled exactly after GRUB behaviour, which has proven to be functional. */
EFI_LOADED_IMAGE_PROTOCOL *parent_loaded_image;
err = BS->HandleProtocol(
parent_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &parent_loaded_image);
if (err != EFI_SUCCESS)
return log_error_status(err, "Cannot get parent loaded image: %m");
_cleanup_free_ KERNEL_FILE_PATH *kernel_file_path = xnew(KERNEL_FILE_PATH, 1);
*kernel_file_path = (KERNEL_FILE_PATH) {
.memmap_path = {
.Header = {
.Type = HARDWARE_DEVICE_PATH,
.SubType = HW_MEMMAP_DP,
.Length = sizeof(MEMMAP_DEVICE_PATH),
},
.MemoryType = EfiLoaderData,
.StartingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base),
.EndingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len,
},
.end_path = DEVICE_PATH_END_NODE,
};
/* If shim provides LoadImage, it comes from the new SHIM_IMAGE_LOADER interface added in shim 16,
* and implements the following:
* - shim hashes PE sections of PE binaries it authenticates and stores the hashes in a global
@@ -241,7 +238,8 @@ EFI_STATUS linux_exec(
compat_entry_point,
cmdline,
kernel,
initrd);
initrd,
kernel_file_path);
err = pe_kernel_check_no_relocation(kernel->iov_base);
if (err != EFI_SUCCESS)
@@ -300,26 +298,13 @@ EFI_STATUS linux_exec(
}
}
_cleanup_free_ KERNEL_FILE_PATH *kernel_file_path = xnew(KERNEL_FILE_PATH, 1);
*kernel_file_path = (KERNEL_FILE_PATH) {
.memmap_path = {
.Header = {
.Type = HARDWARE_DEVICE_PATH,
.SubType = HW_MEMMAP_DP,
.Length = sizeof(MEMMAP_DEVICE_PATH),
},
.MemoryType = EfiLoaderData,
.StartingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base),
.EndingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len,
},
.end_path = {
.Type = END_DEVICE_PATH_TYPE,
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
.Length = sizeof(EFI_DEVICE_PATH),
},
};
/* Patch the parent_image(_handle) and parent_loaded_image for the kernel image we are about to execute.
* We have to do this, because if kernel stub code passes its own handle to certain firmware functions,
* the firmware could cast EFI_LOADED_IMAGE_PROTOCOL * to a larger struct to access its own private data,
* and if we allocated a smaller struct, that could cause problems.
* This is modeled exactly after GRUB behaviour, which has proven to be functional. */
EFI_LOADED_IMAGE_PROTOCOL original_parent_loaded_image = *parent_loaded_image;
parent_loaded_image->FilePath = &kernel_file_path->memmap_path.Header;
parent_loaded_image->ImageBase = loaded_kernel;
parent_loaded_image->ImageSize = kernel_size_in_memory;
@@ -346,6 +331,9 @@ EFI_STATUS linux_exec(
err = compat_entry(parent_image, ST);
}
/* Restore */
*parent_loaded_image = original_parent_loaded_image;
/* On failure we'll free the buffers. EDK2 requires the memory buffers to be writable and
* non-executable, as in some configurations it will overwrite them with a fixed pattern, so if the
* attributes are not restored FreePages() will crash. */