301 lines
9.7 KiB
Diff
301 lines
9.7 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Chris Coulson <chris.coulson@canonical.com>
|
||
|
Date: Mon, 2 May 2022 17:04:23 +0200
|
||
|
Subject: [PATCH] loader/i386/efi/linux: Use grub_loader_set_ex
|
||
|
|
||
|
This ports the linuxefi loader to use grub_loader_set_ex in order to fix
|
||
|
a use-after-fre bug that occurs when grub_cmd_linux is executed more than
|
||
|
once before a boot attempt is performed.
|
||
|
|
||
|
This is more complicated than for the chainloader command, as the initrd
|
||
|
command needs access to the loader state. To solve this, the linuxefi
|
||
|
module registers a dummy initrd command at startup that returns an error.
|
||
|
The linuxefi command then registers a proper initrd command with a higher
|
||
|
priority that is passed the loader state.
|
||
|
|
||
|
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
|
||
|
(cherry picked from commit 7cf736436b4c934df5ddfa6f44b46a7e07d99fdc)
|
||
|
[rharwood/pjones: set kernel_size in context]
|
||
|
(cherry picked from commit 9c056391f7a36ea480de9a759c12e55a90f2040a)
|
||
|
[rharwood: verifying twice]
|
||
|
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
|
||
|
(cherry picked from commit df804892f1a754d88a9779320f9429bf40d2a1b3)
|
||
|
(cherry picked from commit d1b506f6c910b96ad47a20247b438c6402a74948)
|
||
|
---
|
||
|
grub-core/loader/i386/efi/linux.c | 146 +++++++++++++++++++++++---------------
|
||
|
1 file changed, 87 insertions(+), 59 deletions(-)
|
||
|
|
||
|
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
|
||
|
index c9a2b47370..77a0734786 100644
|
||
|
--- a/grub-core/loader/i386/efi/linux.c
|
||
|
+++ b/grub-core/loader/i386/efi/linux.c
|
||
|
@@ -34,13 +34,19 @@
|
||
|
GRUB_MOD_LICENSE ("GPLv3+");
|
||
|
|
||
|
static grub_dl_t my_mod;
|
||
|
-static int loaded;
|
||
|
-static void *kernel_mem;
|
||
|
-static grub_uint64_t kernel_size;
|
||
|
-static void *initrd_mem;
|
||
|
-static grub_uint32_t handover_offset;
|
||
|
-struct linux_kernel_params *params;
|
||
|
-static char *linux_cmdline;
|
||
|
+
|
||
|
+static grub_command_t cmd_linux, cmd_initrd;
|
||
|
+static grub_command_t cmd_linuxefi, cmd_initrdefi;
|
||
|
+
|
||
|
+struct grub_linuxefi_context {
|
||
|
+ void *kernel_mem;
|
||
|
+ grub_uint64_t kernel_size;
|
||
|
+ grub_uint32_t handover_offset;
|
||
|
+ struct linux_kernel_params *params;
|
||
|
+ char *cmdline;
|
||
|
+
|
||
|
+ void *initrd_mem;
|
||
|
+};
|
||
|
|
||
|
#define MIN(a, b) \
|
||
|
({ typeof (a) _a = (a); \
|
||
|
@@ -123,25 +129,32 @@ kernel_alloc(grub_efi_uintn_t size, const char * const errmsg)
|
||
|
}
|
||
|
|
||
|
static grub_err_t
|
||
|
-grub_linuxefi_boot (void)
|
||
|
+grub_linuxefi_boot (void *data)
|
||
|
{
|
||
|
+ struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
|
||
|
+
|
||
|
asm volatile ("cli");
|
||
|
|
||
|
- return grub_efi_linux_boot ((char *)kernel_mem,
|
||
|
- handover_offset,
|
||
|
- params);
|
||
|
+ return grub_efi_linux_boot ((char *)context->kernel_mem,
|
||
|
+ context->handover_offset,
|
||
|
+ context->params);
|
||
|
}
|
||
|
|
||
|
static grub_err_t
|
||
|
-grub_linuxefi_unload (void)
|
||
|
+grub_linuxefi_unload (void *data)
|
||
|
{
|
||
|
+ struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data;
|
||
|
+ struct linux_kernel_params *params = context->params;
|
||
|
+
|
||
|
grub_dl_unref (my_mod);
|
||
|
- loaded = 0;
|
||
|
|
||
|
- kernel_free(initrd_mem, params->ramdisk_size);
|
||
|
- kernel_free(linux_cmdline, params->cmdline_size + 1);
|
||
|
- kernel_free(kernel_mem, kernel_size);
|
||
|
- kernel_free(params, sizeof(*params));
|
||
|
+ kernel_free (context->initrd_mem, params->ramdisk_size);
|
||
|
+ kernel_free (context->cmdline, params->cmdline_size + 1);
|
||
|
+ kernel_free (context->kernel_mem, context->kernel_size);
|
||
|
+ kernel_free (params, sizeof(*params));
|
||
|
+ cmd_initrd->data = 0;
|
||
|
+ cmd_initrdefi->data = 0;
|
||
|
+ grub_free (context);
|
||
|
|
||
|
return GRUB_ERR_NONE;
|
||
|
}
|
||
|
@@ -188,13 +201,14 @@ read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len)
|
||
|
#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull))
|
||
|
|
||
|
static grub_err_t
|
||
|
-grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||
|
- int argc, char *argv[])
|
||
|
+grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
|
||
|
{
|
||
|
grub_file_t *files = 0;
|
||
|
int i, nfiles = 0;
|
||
|
grub_size_t size = 0;
|
||
|
grub_uint8_t *ptr;
|
||
|
+ struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data;
|
||
|
+ struct linux_kernel_params *params;
|
||
|
|
||
|
if (argc == 0)
|
||
|
{
|
||
|
@@ -202,12 +216,14 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
- if (!loaded)
|
||
|
+ if (!context)
|
||
|
{
|
||
|
grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first"));
|
||
|
goto fail;
|
||
|
}
|
||
|
|
||
|
+ params = context->params;
|
||
|
+
|
||
|
files = grub_calloc (argc, sizeof (files[0]));
|
||
|
if (!files)
|
||
|
goto fail;
|
||
|
@@ -226,19 +242,19 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
|
||
|
- if (initrd_mem == NULL)
|
||
|
+ context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
|
||
|
+ if (context->initrd_mem == NULL)
|
||
|
goto fail;
|
||
|
- grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
|
||
|
+ grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem);
|
||
|
|
||
|
params->ramdisk_size = LOW_U32(size);
|
||
|
- params->ramdisk_image = LOW_U32(initrd_mem);
|
||
|
+ params->ramdisk_image = LOW_U32(context->initrd_mem);
|
||
|
#if defined(__x86_64__)
|
||
|
params->ext_ramdisk_size = HIGH_U32(size);
|
||
|
- params->ext_ramdisk_image = HIGH_U32(initrd_mem);
|
||
|
+ params->ext_ramdisk_image = HIGH_U32(context->initrd_mem);
|
||
|
#endif
|
||
|
|
||
|
- ptr = initrd_mem;
|
||
|
+ ptr = context->initrd_mem;
|
||
|
|
||
|
for (i = 0; i < nfiles; i++)
|
||
|
{
|
||
|
@@ -264,8 +280,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
||
|
grub_file_close (files[i]);
|
||
|
grub_free (files);
|
||
|
|
||
|
- if (initrd_mem && grub_errno)
|
||
|
- grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)initrd_mem,
|
||
|
+ if (context->initrd_mem && grub_errno)
|
||
|
+ grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)context->initrd_mem,
|
||
|
BYTES_TO_PAGES(size));
|
||
|
|
||
|
return grub_errno;
|
||
|
@@ -281,6 +297,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||
|
void *kernel = NULL;
|
||
|
int setup_header_end_offset;
|
||
|
int rc;
|
||
|
+ void *kernel_mem = 0;
|
||
|
+ grub_uint64_t kernel_size = 0;
|
||
|
+ grub_uint32_t handover_offset;
|
||
|
+ struct linux_kernel_params *params = 0;
|
||
|
+ char *cmdline = 0;
|
||
|
+ struct grub_linuxefi_context *context = 0;
|
||
|
|
||
|
grub_dl_ref (my_mod);
|
||
|
|
||
|
@@ -407,27 +429,27 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||
|
grub_dprintf ("linux", "new lh is at %p\n", lh);
|
||
|
|
||
|
grub_dprintf ("linux", "setting up cmdline\n");
|
||
|
- linux_cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
|
||
|
- if (!linux_cmdline)
|
||
|
+ cmdline = kernel_alloc (lh->cmdline_size + 1, N_("can't allocate cmdline"));
|
||
|
+ if (!cmdline)
|
||
|
goto fail;
|
||
|
- grub_dprintf ("linux", "linux_cmdline = %p\n", linux_cmdline);
|
||
|
+ grub_dprintf ("linux", "cmdline = %p\n", cmdline);
|
||
|
|
||
|
- grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||
|
+ grub_memcpy (cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
||
|
grub_create_loader_cmdline (argc, argv,
|
||
|
- linux_cmdline + sizeof (LINUX_IMAGE) - 1,
|
||
|
+ cmdline + sizeof (LINUX_IMAGE) - 1,
|
||
|
lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1),
|
||
|
GRUB_VERIFY_KERNEL_CMDLINE);
|
||
|
|
||
|
- grub_dprintf ("linux", "cmdline:%s\n", linux_cmdline);
|
||
|
+ grub_dprintf ("linux", "cmdline:%s\n", cmdline);
|
||
|
grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n",
|
||
|
- LOW_U32(linux_cmdline));
|
||
|
- lh->cmd_line_ptr = LOW_U32(linux_cmdline);
|
||
|
+ LOW_U32(cmdline));
|
||
|
+ lh->cmd_line_ptr = LOW_U32(cmdline);
|
||
|
#if defined(__x86_64__)
|
||
|
- if ((grub_efi_uintn_t)linux_cmdline > 0xffffffffull)
|
||
|
+ if ((grub_efi_uintn_t)cmdline > 0xffffffffull)
|
||
|
{
|
||
|
grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n",
|
||
|
- HIGH_U32(linux_cmdline));
|
||
|
- params->ext_cmd_line_ptr = HIGH_U32(linux_cmdline);
|
||
|
+ HIGH_U32(cmdline));
|
||
|
+ params->ext_cmd_line_ptr = HIGH_U32(cmdline);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
@@ -452,16 +474,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||
|
}
|
||
|
max_addresses[1].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
|
||
|
max_addresses[2].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS;
|
||
|
- kernel_mem = kernel_alloc (lh->init_size, N_("can't allocate kernel"));
|
||
|
+ kernel_size = lh->init_size;
|
||
|
+ kernel_mem = kernel_alloc (kernel_size, N_("can't allocate kernel"));
|
||
|
restore_addresses();
|
||
|
if (!kernel_mem)
|
||
|
goto fail;
|
||
|
grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem);
|
||
|
|
||
|
- grub_loader_set (grub_linuxefi_boot, grub_linuxefi_unload, 0);
|
||
|
-
|
||
|
- loaded = 1;
|
||
|
-
|
||
|
grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n",
|
||
|
LOW_U32(kernel_mem));
|
||
|
lh->code32_start = LOW_U32(kernel_mem);
|
||
|
@@ -478,33 +497,42 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
||
|
"setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n",
|
||
|
params->ext_loader_type, params->ext_loader_ver);
|
||
|
|
||
|
+ context = grub_zalloc (sizeof (*context));
|
||
|
+ if (!context)
|
||
|
+ goto fail;
|
||
|
+ context->kernel_mem = kernel_mem;
|
||
|
+ context->kernel_size = kernel_size;
|
||
|
+ context->handover_offset = handover_offset;
|
||
|
+ context->params = params;
|
||
|
+ context->cmdline = cmdline;
|
||
|
+
|
||
|
+ grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0);
|
||
|
+
|
||
|
+ cmd_initrd->data = context;
|
||
|
+ cmd_initrdefi->data = context;
|
||
|
+
|
||
|
+ grub_file_close (file);
|
||
|
+ grub_free (kernel);
|
||
|
+ return 0;
|
||
|
+
|
||
|
fail:
|
||
|
if (file)
|
||
|
grub_file_close (file);
|
||
|
|
||
|
- if (grub_errno != GRUB_ERR_NONE)
|
||
|
- {
|
||
|
- grub_dl_unref (my_mod);
|
||
|
- loaded = 0;
|
||
|
- }
|
||
|
+ grub_dl_unref (my_mod);
|
||
|
|
||
|
- if (!loaded)
|
||
|
- {
|
||
|
- if (lh)
|
||
|
- kernel_free (linux_cmdline, lh->cmdline_size + 1);
|
||
|
+ if (lh)
|
||
|
+ kernel_free (cmdline, lh->cmdline_size + 1);
|
||
|
|
||
|
- kernel_free (kernel_mem, kernel_size);
|
||
|
- kernel_free (params, sizeof(*params));
|
||
|
- }
|
||
|
+ kernel_free (kernel_mem, kernel_size);
|
||
|
+ kernel_free (params, sizeof(*params));
|
||
|
|
||
|
+ grub_free (context);
|
||
|
grub_free (kernel);
|
||
|
|
||
|
return grub_errno;
|
||
|
}
|
||
|
|
||
|
-static grub_command_t cmd_linux, cmd_initrd;
|
||
|
-static grub_command_t cmd_linuxefi, cmd_initrdefi;
|
||
|
-
|
||
|
GRUB_MOD_INIT(linux)
|
||
|
{
|
||
|
cmd_linux =
|