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