import grub2-2.06-38.el9

c9-beta imports/c9-beta/grub2-2.06-38.el9
CentOS Sources 8 months ago committed by Stepan Oksanichenko
parent 19b1d3c310
commit 73face5eeb

@ -0,0 +1,111 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Fri, 8 Apr 2022 12:35:28 +1000
Subject: [PATCH] powerpc: do CAS in a more compatible way
I wrongly assumed that the most compatible way to perform CAS
negotiation was to only set the minimum number of vectors required
to ask for more memory. It turns out that this messes up booting
if the minimum VP capacity would be less than the default 10% in
vector 4.
Linux configures the minimum capacity to be 1%, so copy it for that
and for vector 3 which we now need to specify as well.
Signed-off-by: Daniel Axtens <dja@axtens.net>
(cherry picked from commit e6f02ad4e75cd995a8ee2954d28949c415b6cbfe)
---
grub-core/kern/ieee1275/init.c | 54 ++++++++++++++++++++++++------------------
1 file changed, 31 insertions(+), 23 deletions(-)
diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c
index 9704715c83..ef55107467 100644
--- a/grub-core/kern/ieee1275/init.c
+++ b/grub-core/kern/ieee1275/init.c
@@ -298,33 +298,37 @@ grub_ieee1275_total_mem (grub_uint64_t *total)
/* Based on linux - arch/powerpc/kernel/prom_init.c */
struct option_vector2 {
- grub_uint8_t byte1;
- grub_uint16_t reserved;
- grub_uint32_t real_base;
- grub_uint32_t real_size;
- grub_uint32_t virt_base;
- grub_uint32_t virt_size;
- grub_uint32_t load_base;
- grub_uint32_t min_rma;
- grub_uint32_t min_load;
- grub_uint8_t min_rma_percent;
- grub_uint8_t max_pft_size;
+ grub_uint8_t byte1;
+ grub_uint16_t reserved;
+ grub_uint32_t real_base;
+ grub_uint32_t real_size;
+ grub_uint32_t virt_base;
+ grub_uint32_t virt_size;
+ grub_uint32_t load_base;
+ grub_uint32_t min_rma;
+ grub_uint32_t min_load;
+ grub_uint8_t min_rma_percent;
+ grub_uint8_t max_pft_size;
} __attribute__((packed));
struct pvr_entry {
- grub_uint32_t mask;
- grub_uint32_t entry;
+ grub_uint32_t mask;
+ grub_uint32_t entry;
};
struct cas_vector {
- struct {
- struct pvr_entry terminal;
- } pvr_list;
- grub_uint8_t num_vecs;
- grub_uint8_t vec1_size;
- grub_uint8_t vec1;
- grub_uint8_t vec2_size;
- struct option_vector2 vec2;
+ struct {
+ struct pvr_entry terminal;
+ } pvr_list;
+ grub_uint8_t num_vecs;
+ grub_uint8_t vec1_size;
+ grub_uint8_t vec1;
+ grub_uint8_t vec2_size;
+ struct option_vector2 vec2;
+ grub_uint8_t vec3_size;
+ grub_uint16_t vec3;
+ grub_uint8_t vec4_size;
+ grub_uint16_t vec4;
} __attribute__((packed));
/* Call ibm,client-architecture-support to try to get more RMA.
@@ -345,13 +349,17 @@ grub_ieee1275_ibm_cas (void)
} args;
struct cas_vector vector = {
.pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */
- .num_vecs = 2 - 1,
+ .num_vecs = 4 - 1,
.vec1_size = 0,
.vec1 = 0x80, /* ignore */
.vec2_size = 1 + sizeof(struct option_vector2) - 2,
.vec2 = {
0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48
},
+ .vec3_size = 2 - 1,
+ .vec3 = 0x00e0, // ask for FP + VMX + DFP but don't halt if unsatisfied
+ .vec4_size = 2 - 1,
+ .vec4 = 0x0001, // set required minimum capacity % to the lowest value
};
INIT_IEEE1275_COMMON (&args.common, "call-method", 3, 2);
@@ -364,7 +372,7 @@ grub_ieee1275_ibm_cas (void)
args.ihandle = root;
args.cas_addr = (grub_ieee1275_cell_t)&vector;
- grub_printf("Calling ibm,client-architecture-support...");
+ grub_printf("Calling ibm,client-architecture-support from grub...");
IEEE1275_CALL_ENTRY_FN (&args);
grub_printf("done\n");

@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Thu, 24 Mar 2022 14:34:32 +1100
Subject: [PATCH] powerpc: prefix detection: support device names with commas
Frustratingly, the device name itself can contain an embedded comma:
e.g /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b
So my previous approach was wrong: we cannot rely upon the presence
of a comma to say that a partition has been specified!
It turns out for prefixes like (,gpt2)/grub2 we really want to make
up a full (device,partition)/patch prefix, because root discovery code
in 10_linux will reset the root variable and use search to fill it again.
If you have run grub-install, you probably don't have search built in,
and if you don't have prefix containing (device,partition), grub will
construct ($root)$prefix/powerpc-ieee1275/search.mod - but because $root
has just been changed, this will no longer work, and the boot will fail!
Retain the gist of the logic, but instead of looking for a comma, look for
a leading '('. This matches the earlier code better anyway.
There's certainly a better fix to be had. But any time you chose to build
with a bare prefix like '/grub2', you're almost certainly going to build in
search anyway, so this will do.
Signed-off-by: Daniel Axtens <dja@axtens.net>
(cherry picked from commit 80b6eb5e55e6d1a4c9896361e61de31c29e6939d)
---
grub-core/kern/main.c | 27 +++++++++++++++++++++------
1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c
index 2d0d2bbd4c..4c4e6912f9 100644
--- a/grub-core/kern/main.c
+++ b/grub-core/kern/main.c
@@ -242,14 +242,29 @@ grub_set_prefix_and_root (void)
what sorts of paths represent disks with partition tables and those
without partition tables.
- So we act unless there is a comma in the device, which would indicate
- a partition has already been specified.
+ - Frustratingly, the device name itself can contain an embedded comma:
+ /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b
+ So we cannot even rely upon the presence of a comma to say that a
+ partition has been specified!
- (If we only have a path, the code in normal to discover config files
- will try both without partitions and then with any partitions so we
- will cover both CDs and HDs.)
+ If we only have a path in $prefix, the code in normal to discover
+ config files will try all disks, both without partitions and then with
+ any partitions so we will cover both CDs and HDs.
+
+ However, it doesn't then set the prefix to be something like
+ (discovered partition)/path, and so it is fragile against runtime
+ changes to $root. For example some of the stuff done in 10_linux to
+ reload $root sets root differently and then uses search to find it
+ again. If the search module is not built in, when we change root, grub
+ will look in (new root)/path/powerpc-ieee1275, that won't work, and we
+ will not be able to load the search module and the boot will fail.
+
+ This is particularly likely to hit us in the grub-install
+ (,msdos2)/grub2 case, so we act unless the supplied prefix starts with
+ '(', which would likely indicate a partition has already been
+ specified.
*/
- if (grub_strchr (device, ',') == NULL)
+ if (prefix && prefix[0] != '(')
grub_env_set ("prefix", path);
else
#endif

@ -0,0 +1,237 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Stefan Berger <stefanb@linux.ibm.com>
Date: Sun, 15 Mar 2020 12:37:10 -0400
Subject: [PATCH] ibmvtpm: Add support for trusted boot using a vTPM 2.0
Add support for trusted boot using a vTPM 2.0 on the IBM IEEE1275
PowerPC platform. With this patch grub now measures text and binary data
into the TPM's PCRs 8 and 9 in the same way as the x86_64 platform
does.
This patch requires Daniel Axtens's patches for claiming more memory.
For vTPM support to work on PowerVM, system driver levels 1010.30
or 1020.00 are required.
Note: Previous versions of firmware levels with the 2hash-ext-log
API call have a bug that, once this API call is invoked, has the
effect of disabling the vTPM driver under Linux causing an error
message to be displayed in the Linux kernel log. Those users will
have to update their machines to the firmware levels mentioned
above.
Cc: Eric Snowberg <eric.snowberg@oracle.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
(cherry picked from commit d3e5a8e6ecb8b87701135d97f45d27bbfbf731a2)
---
grub-core/Makefile.core.def | 7 ++
grub-core/commands/ieee1275/ibmvtpm.c | 152 ++++++++++++++++++++++++++++++++++
include/grub/ieee1275/ieee1275.h | 3 +
docs/grub.texi | 3 +-
4 files changed, 164 insertions(+), 1 deletion(-)
create mode 100644 grub-core/commands/ieee1275/ibmvtpm.c
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 97abc01f06..407d68f917 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1172,6 +1172,13 @@ module = {
enable = powerpc_ieee1275;
};
+module = {
+ name = tpm;
+ common = commands/tpm.c;
+ ieee1275 = commands/ieee1275/ibmvtpm.c;
+ enable = powerpc_ieee1275;
+};
+
module = {
name = terminal;
common = commands/terminal.c;
diff --git a/grub-core/commands/ieee1275/ibmvtpm.c b/grub-core/commands/ieee1275/ibmvtpm.c
new file mode 100644
index 0000000000..e68b8448bc
--- /dev/null
+++ b/grub-core/commands/ieee1275/ibmvtpm.c
@@ -0,0 +1,152 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ * Copyright (C) 2021 IBM Corporation
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * IBM vTPM support code.
+ */
+
+#include <grub/err.h>
+#include <grub/types.h>
+#include <grub/tpm.h>
+#include <grub/ieee1275/ieee1275.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+
+static grub_ieee1275_ihandle_t tpm_ihandle;
+static grub_uint8_t tpm_version;
+
+#define IEEE1275_IHANDLE_INVALID ((grub_ieee1275_ihandle_t)0)
+
+static void
+tpm_get_tpm_version (void)
+{
+ grub_ieee1275_phandle_t vtpm;
+ char buffer[20];
+
+ if (!grub_ieee1275_finddevice ("/vdevice/vtpm", &vtpm) &&
+ !grub_ieee1275_get_property (vtpm, "compatible", buffer,
+ sizeof (buffer), NULL) &&
+ !grub_strcmp (buffer, "IBM,vtpm20"))
+ tpm_version = 2;
+}
+
+static grub_err_t
+tpm_init (void)
+{
+ static int init_success = 0;
+
+ if (!init_success)
+ {
+ if (grub_ieee1275_open ("/vdevice/vtpm", &tpm_ihandle) < 0) {
+ tpm_ihandle = IEEE1275_IHANDLE_INVALID;
+ return GRUB_ERR_UNKNOWN_DEVICE;
+ }
+
+ init_success = 1;
+
+ tpm_get_tpm_version ();
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+static int
+ibmvtpm_2hash_ext_log (grub_uint8_t pcrindex,
+ grub_uint32_t eventtype,
+ const char *description,
+ grub_size_t description_size,
+ void *buf, grub_size_t size)
+{
+ struct tpm_2hash_ext_log
+ {
+ struct grub_ieee1275_common_hdr common;
+ grub_ieee1275_cell_t method;
+ grub_ieee1275_cell_t ihandle;
+ grub_ieee1275_cell_t size;
+ grub_ieee1275_cell_t buf;
+ grub_ieee1275_cell_t description_size;
+ grub_ieee1275_cell_t description;
+ grub_ieee1275_cell_t eventtype;
+ grub_ieee1275_cell_t pcrindex;
+ grub_ieee1275_cell_t catch_result;
+ grub_ieee1275_cell_t rc;
+ }
+ args;
+
+ INIT_IEEE1275_COMMON (&args.common, "call-method", 8, 2);
+ args.method = (grub_ieee1275_cell_t) "2hash-ext-log";
+ args.ihandle = tpm_ihandle;
+ args.pcrindex = pcrindex;
+ args.eventtype = eventtype;
+ args.description = (grub_ieee1275_cell_t) description;
+ args.description_size = description_size;
+ args.buf = (grub_ieee1275_cell_t) buf;
+ args.size = (grub_ieee1275_cell_t) size;
+
+ if (IEEE1275_CALL_ENTRY_FN (&args) == -1)
+ return -1;
+
+ /*
+ * catch_result is set if firmware does not support 2hash-ext-log
+ * rc is GRUB_IEEE1275_CELL_FALSE (0) on failure
+ */
+ if ((args.catch_result) || args.rc == GRUB_IEEE1275_CELL_FALSE)
+ return -1;
+
+ return 0;
+}
+
+static grub_err_t
+tpm2_log_event (unsigned char *buf,
+ grub_size_t size, grub_uint8_t pcr,
+ const char *description)
+{
+ static int error_displayed = 0;
+ int err;
+
+ err = ibmvtpm_2hash_ext_log (pcr, EV_IPL,
+ description,
+ grub_strlen(description) + 1,
+ buf, size);
+ if (err && !error_displayed)
+ {
+ error_displayed++;
+ return grub_error (GRUB_ERR_BAD_DEVICE,
+ "2HASH-EXT-LOG failed: Firmware is likely too old.\n");
+ }
+
+ return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr,
+ const char *description)
+{
+ grub_err_t err = tpm_init();
+
+ /* Absence of a TPM isn't a failure. */
+ if (err != GRUB_ERR_NONE)
+ return GRUB_ERR_NONE;
+
+ grub_dprintf ("tpm", "log_event, pcr = %d, size = 0x%" PRIxGRUB_SIZE ", %s\n",
+ pcr, size, description);
+
+ if (tpm_version == 2)
+ return tpm2_log_event (buf, size, pcr, description);
+
+ return GRUB_ERR_NONE;
+}
diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h
index e0a6c2ce1e..f4c85265fe 100644
--- a/include/grub/ieee1275/ieee1275.h
+++ b/include/grub/ieee1275/ieee1275.h
@@ -24,6 +24,9 @@
#include <grub/types.h>
#include <grub/machine/ieee1275.h>
+#define GRUB_IEEE1275_CELL_FALSE ((grub_ieee1275_cell_t) 0)
+#define GRUB_IEEE1275_CELL_TRUE ((grub_ieee1275_cell_t) -1)
+
struct grub_ieee1275_mem_region
{
unsigned int start;
diff --git a/docs/grub.texi b/docs/grub.texi
index a4da9c2a1b..c433240f34 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -6221,7 +6221,8 @@ tpm module is loaded. As such it is recommended that the tpm module be built
into @file{core.img} in order to avoid a potential gap in measurement between
@file{core.img} being loaded and the tpm module being loaded.
-Measured boot is currently only supported on EFI platforms.
+Measured boot is currently only supported on EFI and IBM IEEE1275 PowerPC
+platforms.
@node Lockdown
@section Lockdown when booting on a secure setup

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Diego Domingos <diegdo@br.ibm.com>
Date: Thu, 24 Mar 2022 13:14:42 -0400
Subject: [PATCH] make ofdisk_retries optional
The feature Retry on Fail added to GRUB can cause a LPM to take
longer if the SAN is slow.
When a LPM to external site occur, the path of the disk can change
and thus the disk search function on grub can take some time since
it is used as a hint. This can cause the Retry on Fail feature to
try to access the disk 20x times (since this is hardcoded number)
and, if the SAN is slow, the boot time can increase a lot.
In some situations not acceptable.
The following patch enables a configuration at user space of the
maximum number of retries we want for this feature.
The variable ofdisk_retries should be set using grub2-editenv
and will be checked by retry function. If the variable is not set,
so the default number of retries will be used instead.
(cherry picked from commit 4c5c7563f45a6410667ca08bcbfac4ab79d7de31)
---
include/grub/ieee1275/ofdisk.h | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h
index 7d2d540930..0074d55eee 100644
--- a/include/grub/ieee1275/ofdisk.h
+++ b/include/grub/ieee1275/ofdisk.h
@@ -25,7 +25,12 @@ extern void grub_ofdisk_fini (void);
#define MAX_RETRIES 20
-#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) unsigned retry_i=0;for(retry_i=0; retry_i < MAX_RETRIES; retry_i++){ \
+#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) \
+ unsigned max_retries = MAX_RETRIES; \
+ if(grub_env_get("ofdisk_retries") != NULL) \
+ max_retries = grub_strtoul(grub_env_get("ofdisk_retries"), 0, 10)+1; \
+ grub_dprintf("ofdisk","MAX_RETRIES set to %u\n",max_retries); \
+ unsigned retry_i=0;for(retry_i=0; retry_i < max_retries; retry_i++){ \
if(!grub_ieee1275_open(device, last_ihandle)) \
break; \
grub_dprintf("ofdisk","Opening disk %s failed. Retrying...\n",device); }

@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Thu, 28 Apr 2022 21:53:36 +0100
Subject: [PATCH] loader/efi/chainloader: grub_load_and_start_image doesn't
load and start
grub_load_and_start_image only loads an image - it still requires the
caller to start it. This renames it to grub_load_image.
It's called from 2 places:
- grub_cmd_chainloader when not using the shim protocol.
- grub_secureboot_chainloader_boot if handle_image returns an error.
In this case, the image is loaded and then nothing else happens which
seems strange. I assume the intention is that it falls back to LoadImage
and StartImage if handle_image fails, so I've made it do that.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
(cherry picked from commit b4d70820a65c00561045856b7b8355461a9545f6)
(cherry picked from commit 05b16a6be50b1910609740a66b561276fa490538)
---
grub-core/loader/efi/chainloader.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index 3af6b12292..39158e679e 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -841,7 +841,7 @@ grub_secureboot_chainloader_unload (void)
}
static grub_err_t
-grub_load_and_start_image(void *boot_image)
+grub_load_image(void *boot_image)
{
grub_efi_boot_services_t *b;
grub_efi_status_t status;
@@ -883,13 +883,23 @@ grub_load_and_start_image(void *boot_image)
static grub_err_t
grub_secureboot_chainloader_boot (void)
{
+ grub_efi_boot_services_t *b;
int rc;
+
rc = handle_image ((void *)(unsigned long)address, fsize);
if (rc == 0)
{
- grub_load_and_start_image((void *)(unsigned long)address);
+ /* We weren't able to attempt to execute the image, so fall back
+ * to LoadImage / StartImage.
+ */
+ rc = grub_load_image((void *)(unsigned long)address);
+ if (rc == 0)
+ grub_chainloader_boot ();
}
+ b = grub_efi_system_table->boot_services;
+ efi_call_1 (b->unload_image, image_handle);
+
grub_loader_unset ();
return grub_errno;
}
@@ -1094,7 +1104,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
}
else if (rc == 0)
{
- grub_load_and_start_image(boot_image);
+ grub_load_image(boot_image);
grub_file_close (file);
grub_device_close (dev);
grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);

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

@ -0,0 +1,159 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Fri, 29 Apr 2022 21:16:02 +0100
Subject: [PATCH] commands/boot: Add API to pass context to loader
Loaders rely on global variables for saving context which is consumed
in the boot hook and freed in the unload hook. In the case where a loader
command is executed twice, calling grub_loader_set a second time executes
the unload hook, but in some cases this runs when the loader's global
context has already been updated, resulting in the updated context being
freed and potential use-after-free bugs when the boot hook is subsequently
called.
This adds a new API (grub_loader_set_ex) which allows a loader to specify
context that is passed to its boot and unload hooks. This is an alternative
to requiring that loaders call grub_loader_unset before mutating their
global context.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
(cherry picked from commit 4322a64dde7e8fedb58e50b79408667129d45dd3)
(cherry picked from commit 937ad0e2159b6b8cb0d2ce3515da3a8b797c7927)
---
grub-core/commands/boot.c | 66 +++++++++++++++++++++++++++++++++++++++++------
include/grub/loader.h | 5 ++++
2 files changed, 63 insertions(+), 8 deletions(-)
diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c
index bbca81e947..53691a62d9 100644
--- a/grub-core/commands/boot.c
+++ b/grub-core/commands/boot.c
@@ -27,10 +27,20 @@
GRUB_MOD_LICENSE ("GPLv3+");
-static grub_err_t (*grub_loader_boot_func) (void);
-static grub_err_t (*grub_loader_unload_func) (void);
+static grub_err_t (*grub_loader_boot_func) (void *);
+static grub_err_t (*grub_loader_unload_func) (void *);
+static void *grub_loader_context;
static int grub_loader_flags;
+struct grub_simple_loader_hooks
+{
+ grub_err_t (*boot) (void);
+ grub_err_t (*unload) (void);
+};
+
+/* Don't heap allocate this to avoid making grub_loader_set fallible. */
+static struct grub_simple_loader_hooks simple_loader_hooks;
+
struct grub_preboot
{
grub_err_t (*preboot_func) (int);
@@ -44,6 +54,29 @@ static int grub_loader_loaded;
static struct grub_preboot *preboots_head = 0,
*preboots_tail = 0;
+static grub_err_t
+grub_simple_boot_hook (void *context)
+{
+ struct grub_simple_loader_hooks *hooks;
+
+ hooks = (struct grub_simple_loader_hooks *) context;
+ return hooks->boot ();
+}
+
+static grub_err_t
+grub_simple_unload_hook (void *context)
+{
+ struct grub_simple_loader_hooks *hooks;
+ grub_err_t ret;
+
+ hooks = (struct grub_simple_loader_hooks *) context;
+
+ ret = hooks->unload ();
+ grub_memset (hooks, 0, sizeof (*hooks));
+
+ return ret;
+}
+
int
grub_loader_is_loaded (void)
{
@@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd)
}
void
-grub_loader_set (grub_err_t (*boot) (void),
- grub_err_t (*unload) (void),
- int flags)
+grub_loader_set_ex (grub_err_t (*boot) (void *),
+ grub_err_t (*unload) (void *),
+ void *context,
+ int flags)
{
if (grub_loader_loaded && grub_loader_unload_func)
- grub_loader_unload_func ();
+ grub_loader_unload_func (grub_loader_context);
grub_loader_boot_func = boot;
grub_loader_unload_func = unload;
+ grub_loader_context = context;
grub_loader_flags = flags;
grub_loader_loaded = 1;
}
+void
+grub_loader_set (grub_err_t (*boot) (void),
+ grub_err_t (*unload) (void),
+ int flags)
+{
+ grub_loader_set_ex (grub_simple_boot_hook,
+ grub_simple_unload_hook,
+ &simple_loader_hooks,
+ flags);
+
+ simple_loader_hooks.boot = boot;
+ simple_loader_hooks.unload = unload;
+}
+
void
grub_loader_unset(void)
{
if (grub_loader_loaded && grub_loader_unload_func)
- grub_loader_unload_func ();
+ grub_loader_unload_func (grub_loader_context);
grub_loader_boot_func = 0;
grub_loader_unload_func = 0;
+ grub_loader_context = 0;
grub_loader_loaded = 0;
}
@@ -158,7 +208,7 @@ grub_loader_boot (void)
return err;
}
}
- err = (grub_loader_boot_func) ();
+ err = (grub_loader_boot_func) (grub_loader_context);
for (cur = preboots_tail; cur; cur = cur->prev)
if (! err)
diff --git a/include/grub/loader.h b/include/grub/loader.h
index b208642821..1846fa6c5f 100644
--- a/include/grub/loader.h
+++ b/include/grub/loader.h
@@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
grub_err_t (*unload) (void),
int flags);
+void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *),
+ grub_err_t (*unload) (void *),
+ void *context,
+ int flags);
+
/* Unset current loader, if any. */
void EXPORT_FUNC (grub_loader_unset) (void);

@ -0,0 +1,148 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Fri, 29 Apr 2022 21:30:56 +0100
Subject: [PATCH] loader/efi/chainloader: Use grub_loader_set_ex
This ports the EFI chainloader to use grub_loader_set_ex in order to fix
a use-after-free bug that occurs when grub_cmd_chainloader is executed
more than once before a boot attempt is performed.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
(cherry picked from commit 4b7f0402b7cb0f67a93be736f2b75b818d7f44c9)
(cherry picked from commit fc1a79bf0e0bc019362ace46d908a92b48dcd55b)
[rharwood: context sludge from previous commit]
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
---
grub-core/loader/efi/chainloader.c | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index 0717ce0478..8ef508beca 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -48,8 +48,6 @@ GRUB_MOD_LICENSE ("GPLv3+");
static grub_dl_t my_mod;
-static grub_efi_handle_t image_handle;
-
struct grub_secureboot_chainloader_context {
grub_efi_physical_address_t address;
grub_efi_uintn_t pages;
@@ -59,7 +57,6 @@ struct grub_secureboot_chainloader_context {
grub_ssize_t cmdline_len;
grub_efi_handle_t dev_handle;
};
-static struct grub_secureboot_chainloader_context *sb_context;
static grub_err_t
grub_start_image (grub_efi_handle_t handle)
@@ -98,11 +95,14 @@ grub_start_image (grub_efi_handle_t handle)
}
static grub_err_t
-grub_chainloader_unload (void)
+grub_chainloader_unload (void *context)
{
+ grub_efi_handle_t image_handle;
grub_efi_loaded_image_t *loaded_image;
grub_efi_boot_services_t *b;
+ image_handle = (grub_efi_handle_t) context;
+
loaded_image = grub_efi_get_loaded_image (image_handle);
if (loaded_image != NULL)
grub_free (loaded_image->load_options);
@@ -115,10 +115,12 @@ grub_chainloader_unload (void)
}
static grub_err_t
-grub_chainloader_boot (void)
+grub_chainloader_boot (void *context)
{
+ grub_efi_handle_t image_handle;
grub_err_t err;
+ image_handle = (grub_efi_handle_t) context;
err = grub_start_image (image_handle);
grub_loader_unset ();
@@ -839,15 +841,17 @@ error_exit:
}
static grub_err_t
-grub_secureboot_chainloader_unload (void)
+grub_secureboot_chainloader_unload (void *context)
{
+ struct grub_secureboot_chainloader_context *sb_context;
+
+ sb_context = (struct grub_secureboot_chainloader_context *) context;
+
grub_efi_free_pages (sb_context->address, sb_context->pages);
grub_free (sb_context->file_path);
grub_free (sb_context->cmdline);
grub_free (sb_context);
- sb_context = 0;
-
grub_dl_unref (my_mod);
return GRUB_ERR_NONE;
}
@@ -896,12 +900,15 @@ grub_load_image(grub_efi_device_path_t *file_path, void *boot_image,
}
static grub_err_t
-grub_secureboot_chainloader_boot (void)
+grub_secureboot_chainloader_boot (void *context)
{
+ struct grub_secureboot_chainloader_context *sb_context;
grub_efi_boot_services_t *b;
int rc;
grub_efi_handle_t handle = 0;
+ sb_context = (struct grub_secureboot_chainloader_context *) context;
+
rc = handle_image (sb_context);
if (rc == 0)
{
@@ -943,6 +950,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
grub_efi_char16_t *cmdline = 0;
grub_ssize_t cmdline_len = 0;
grub_efi_handle_t dev_handle = 0;
+ grub_efi_handle_t image_handle = 0;
+ struct grub_secureboot_chainloader_context *sb_context = 0;
if (argc == 0)
return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
@@ -1130,8 +1139,8 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
grub_file_close (file);
grub_device_close (dev);
- grub_loader_set (grub_secureboot_chainloader_boot,
- grub_secureboot_chainloader_unload, 0);
+ grub_loader_set_ex (grub_secureboot_chainloader_boot,
+ grub_secureboot_chainloader_unload, sb_context, 0);
return 0;
}
else if (rc == 0)
@@ -1145,7 +1154,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
efi_call_2 (b->free_pages, address, pages);
grub_free (file_path);
- grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+ grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0);
return 0;
}
@@ -1173,10 +1182,7 @@ fail:
grub_free (cmdline);
if (image_handle != 0)
- {
- efi_call_1 (b->unload_image, image_handle);
- image_handle = 0;
- }
+ efi_call_1 (b->unload_image, image_handle);
grub_dl_unref (my_mod);

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Mon, 2 May 2022 14:39:31 +0200
Subject: [PATCH] loader/i386/efi/linux: Avoid a use-after-free in the linuxefi
loader
In some error paths in grub_cmd_linux, the pointer to lh may be
dereferenced after the buffer it points to has been freed. There aren't
any security implications from this because nothing else uses the
allocator after the buffer is freed and before the pointer is
dereferenced, but fix it anyway.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
(cherry picked from commit 8224f5a71af94bec8697de17e7e579792db9f9e2)
(cherry picked from commit 4744b62e20d07674017213ac54d7442d679f9d1a)
---
grub-core/loader/i386/efi/linux.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
index 3cf0f9b330..08c9fe6b0e 100644
--- a/grub-core/loader/i386/efi/linux.c
+++ b/grub-core/loader/i386/efi/linux.c
@@ -478,9 +478,6 @@ fail:
if (file)
grub_file_close (file);
- if (kernel)
- grub_free (kernel);
-
if (grub_errno != GRUB_ERR_NONE)
{
grub_dl_unref (my_mod);
@@ -496,6 +493,8 @@ fail:
kernel_free (params, sizeof(*params));
}
+ grub_free (kernel);
+
return grub_errno;
}

@ -0,0 +1,298 @@
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>
---
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 08c9fe6b0e..9e25e51ccf 100644
--- a/grub-core/loader/i386/efi/linux.c
+++ b/grub-core/loader/i386/efi/linux.c
@@ -35,13 +35,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); \
@@ -124,25 +130,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;
}
@@ -189,13 +202,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)
{
@@ -203,12 +217,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++)
{
@@ -262,8 +278,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;
@@ -279,6 +295,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);
@@ -403,27 +425,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
@@ -448,16 +470,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);
@@ -474,33 +493,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 =

@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Tue, 3 May 2022 09:47:35 +0200
Subject: [PATCH] loader/i386/efi/linux: Fix a memory leak in the initrd
command
Subsequent invocations of the initrd command result in the previous
initrd being leaked, so fix that.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
(cherry picked from commit d98af31ce1e31bb22163960d53f5eb28c66582a0)
(cherry picked from commit 62234d6a00e6d1dd8e017ff161d359feb5234082)
---
grub-core/loader/i386/efi/linux.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
index 9e25e51ccf..d24553a79d 100644
--- a/grub-core/loader/i386/efi/linux.c
+++ b/grub-core/loader/i386/efi/linux.c
@@ -210,6 +210,7 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
grub_uint8_t *ptr;
struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data;
struct linux_kernel_params *params;
+ void *initrd_mem = 0;
if (argc == 0)
{
@@ -242,19 +243,19 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
}
}
- context->initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
- if (context->initrd_mem == NULL)
+ initrd_mem = kernel_alloc(size, N_("can't allocate initrd"));
+ if (initrd_mem == NULL)
goto fail;
- grub_dprintf ("linux", "initrd_mem = %p\n", context->initrd_mem);
+ grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem);
params->ramdisk_size = LOW_U32(size);
- params->ramdisk_image = LOW_U32(context->initrd_mem);
+ params->ramdisk_image = LOW_U32(initrd_mem);
#if defined(__x86_64__)
params->ext_ramdisk_size = HIGH_U32(size);
- params->ext_ramdisk_image = HIGH_U32(context->initrd_mem);
+ params->ext_ramdisk_image = HIGH_U32(initrd_mem);
#endif
- ptr = context->initrd_mem;
+ ptr = initrd_mem;
for (i = 0; i < nfiles; i++)
{
@@ -271,6 +272,9 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
ptr += ALIGN_UP_OVERHEAD (cursize, 4);
}
+ kernel_free(context->initrd_mem, params->ramdisk_size);
+
+ context->initrd_mem = initrd_mem;
params->ramdisk_size = size;
fail:
@@ -278,9 +282,8 @@ grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[])
grub_file_close (files[i]);
grub_free (files);
- 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));
+ if (initrd_mem && grub_errno)
+ kernel_free (initrd_mem, size);
return grub_errno;
}

@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Julian Andres Klode <julian.klode@canonical.com>
Date: Thu, 2 Dec 2021 15:03:53 +0100
Subject: [PATCH] kern/efi/sb: Reject non-kernel files in the shim_lock
verifier
We must not allow other verifiers to pass things like the GRUB modules.
Instead of maintaining a blocklist, maintain an allowlist of things
that we do not care about.
This allowlist really should be made reusable, and shared by the
lockdown verifier, but this is the minimal patch addressing
security concerns where the TPM verifier was able to mark modules
as verified (or the OpenPGP verifier for that matter), when it
should not do so on shim-powered secure boot systems.
Fixes: CVE-2022-28735
Signed-off-by: Julian Andres Klode <julian.klode@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit fa61ad69861c1cb3f68bf853d78fae7fd93986a0)
(cherry picked from commit f418191e01b38a635319a26925cf345523d4440c)
---
grub-core/kern/efi/sb.c | 39 ++++++++++++++++++++++++++++++++++++---
include/grub/verify.h | 1 +
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index c52ec6226a..89c4bb3fd1 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -119,10 +119,11 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
void **context __attribute__ ((unused)),
enum grub_verify_flags *flags)
{
- *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+ *flags = GRUB_VERIFY_FLAGS_NONE;
switch (type & GRUB_FILE_TYPE_MASK)
{
+ /* Files we check. */
case GRUB_FILE_TYPE_LINUX_KERNEL:
case GRUB_FILE_TYPE_MULTIBOOT_KERNEL:
case GRUB_FILE_TYPE_BSD_KERNEL:
@@ -130,11 +131,43 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
case GRUB_FILE_TYPE_PLAN9_KERNEL:
case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE:
*flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
+ return GRUB_ERR_NONE;
- /* Fall through. */
+ /* Files that do not affect secureboot state. */
+ case GRUB_FILE_TYPE_NONE:
+ case GRUB_FILE_TYPE_LOOPBACK:
+ case GRUB_FILE_TYPE_LINUX_INITRD:
+ case GRUB_FILE_TYPE_OPENBSD_RAMDISK:
+ case GRUB_FILE_TYPE_XNU_RAMDISK:
+ case GRUB_FILE_TYPE_SIGNATURE:
+ case GRUB_FILE_TYPE_PUBLIC_KEY:
+ case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST:
+ case GRUB_FILE_TYPE_PRINT_BLOCKLIST:
+ case GRUB_FILE_TYPE_TESTLOAD:
+ case GRUB_FILE_TYPE_GET_SIZE:
+ case GRUB_FILE_TYPE_FONT:
+ case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY:
+ case GRUB_FILE_TYPE_CAT:
+ case GRUB_FILE_TYPE_HEXCAT:
+ case GRUB_FILE_TYPE_CMP:
+ case GRUB_FILE_TYPE_HASHLIST:
+ case GRUB_FILE_TYPE_TO_HASH:
+ case GRUB_FILE_TYPE_KEYBOARD_LAYOUT:
+ case GRUB_FILE_TYPE_PIXMAP:
+ case GRUB_FILE_TYPE_GRUB_MODULE_LIST:
+ case GRUB_FILE_TYPE_CONFIG:
+ case GRUB_FILE_TYPE_THEME:
+ case GRUB_FILE_TYPE_GETTEXT_CATALOG:
+ case GRUB_FILE_TYPE_FS_SEARCH:
+ case GRUB_FILE_TYPE_LOADENV:
+ case GRUB_FILE_TYPE_SAVEENV:
+ case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
+ *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+ return GRUB_ERR_NONE;
+ /* Other files. */
default:
- return GRUB_ERR_NONE;
+ return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy"));
}
}
diff --git a/include/grub/verify.h b/include/grub/verify.h
index cd129c398f..672ae16924 100644
--- a/include/grub/verify.h
+++ b/include/grub/verify.h
@@ -24,6 +24,7 @@
enum grub_verify_flags
{
+ GRUB_VERIFY_FLAGS_NONE = 0,
GRUB_VERIFY_FLAGS_SKIP_VERIFICATION = 1,
GRUB_VERIFY_FLAGS_SINGLE_CHUNK = 2,
/* Defer verification to another authority. */

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Fri, 25 Jun 2021 02:19:05 +1000
Subject: [PATCH] kern/file: Do not leak device_name on error in
grub_file_open()
If we have an error in grub_file_open() before we free device_name, we
will leak it.
Free device_name in the error path and null out the pointer in the good
path once we free it there.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 1499a5068839fa37cb77ecef4b5bdacbd1ed12ea)
(cherry picked from commit 2ec50b289d8b24922433439533113087f111f110)
---
grub-core/kern/file.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
index e19aea3e51..ed69fc0f0f 100644
--- a/grub-core/kern/file.c
+++ b/grub-core/kern/file.c
@@ -81,6 +81,7 @@ grub_file_open (const char *name, enum grub_file_type type)
device = grub_device_open (device_name);
grub_free (device_name);
+ device_name = NULL;
if (! device)
goto fail;
@@ -135,6 +136,7 @@ grub_file_open (const char *name, enum grub_file_type type)
return file;
fail:
+ grub_free (device_name);
if (device)
grub_device_close (device);

@ -0,0 +1,199 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Axtens <dja@axtens.net>
Date: Tue, 6 Jul 2021 14:02:55 +1000
Subject: [PATCH] video/readers/png: Abort sooner if a read operation fails
Fuzzing revealed some inputs that were taking a long time, potentially
forever, because they did not bail quickly upon encountering an I/O error.
Try to catch I/O errors sooner and bail out.
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
(cherry picked from commit 882be97d1df6449b9fd4d593f0cb70005fde3494)
(cherry picked from commit 3f6fc3ebfd58fcdb3fe6c2f7a5a4fa05772ae786)
---
grub-core/video/readers/png.c | 55 ++++++++++++++++++++++++++++++++++++-------
1 file changed, 47 insertions(+), 8 deletions(-)
diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index 0157ff7420..e2a6b1cf3c 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -142,6 +142,7 @@ static grub_uint8_t
grub_png_get_byte (struct grub_png_data *data)
{
grub_uint8_t r;
+ grub_ssize_t bytes_read = 0;
if ((data->inside_idat) && (data->idat_remain == 0))
{
@@ -175,7 +176,14 @@ grub_png_get_byte (struct grub_png_data *data)
}
r = 0;
- grub_file_read (data->file, &r, 1);
+ bytes_read = grub_file_read (data->file, &r, 1);
+
+ if (bytes_read != 1)
+ {