From 72b1809bbc5c2b5e37765ca58be68b745985597c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 15:52:00 +0200 Subject: [PATCH 01/36] macro: move sizeof_field() macro into src/fundamental/ Let's make this macro available for our EFI code too. --- src/basic/macro.h | 3 --- src/fundamental/macro-fundamental.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/basic/macro.h b/src/basic/macro.h index 19d5039fd3..72910d9c38 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -218,9 +218,6 @@ static inline int __coverity_check_and_return__(int condition) { #define char_array_0(x) x[sizeof(x)-1] = 0; -#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) -#define endoffsetof_field(struct_type, member) (offsetof(struct_type, member) + sizeof_field(struct_type, member)) - /* Maximum buffer size needed for formatting an unsigned integer type as hex, including space for '0x' * prefix and trailing NUL suffix. */ #define HEXADECIMAL_STR_MAX(type) (2 + sizeof(type) * 2 + 1) diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index bbfdcdb218..2595f5501c 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -546,3 +546,6 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) { #else #define DECLARE_SBAT(text) #endif + +#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) +#define endoffsetof_field(struct_type, member) (offsetof(struct_type, member) + sizeof_field(struct_type, member)) From ba81c365f8ac56b2cc0ea271d1a7d1e66a9de282 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 15:54:47 +0200 Subject: [PATCH 02/36] macro: also move FOREACH_ARRAY()/FOREACH_ELEMENT() to fundamental This is also very useful in EFI code. --- src/basic/macro.h | 12 ------------ src/fundamental/macro-fundamental.h | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/basic/macro.h b/src/basic/macro.h index 72910d9c38..8343528e85 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -263,18 +263,6 @@ static inline int __coverity_check_and_return__(int condition) { /* Pointers range from NULL to POINTER_MAX */ #define POINTER_MAX ((void*) UINTPTR_MAX) -#define _FOREACH_ARRAY(i, array, num, m, end) \ - for (typeof(array[0]) *i = (array), *end = ({ \ - typeof(num) m = (num); \ - (i && m > 0) ? i + m : NULL; \ - }); end && i < end; i++) - -#define FOREACH_ARRAY(i, array, num) \ - _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ)) - -#define FOREACH_ELEMENT(i, array) \ - FOREACH_ARRAY(i, array, ELEMENTSOF(array)) - #define _DEFINE_TRIVIAL_REF_FUNC(type, name, scope) \ scope type *name##_ref(type *p) { \ if (!p) \ diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 2595f5501c..38230c5f19 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -549,3 +549,15 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) { #define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) #define endoffsetof_field(struct_type, member) (offsetof(struct_type, member) + sizeof_field(struct_type, member)) + +#define _FOREACH_ARRAY(i, array, num, m, end) \ + for (typeof(array[0]) *i = (array), *end = ({ \ + typeof(num) m = (num); \ + (i && m > 0) ? i + m : NULL; \ + }); end && i < end; i++) + +#define FOREACH_ARRAY(i, array, num) \ + _FOREACH_ARRAY(i, array, num, UNIQ_T(m, UNIQ), UNIQ_T(end, UNIQ)) + +#define FOREACH_ELEMENT(i, array) \ + FOREACH_ARRAY(i, array, ELEMENTSOF(array)) From 515e1e6d3538d2e9efdc594186c4450fbccb44ad Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 16:08:50 +0200 Subject: [PATCH 03/36] macro: move PTR_TO_SIZE() macros to fundamental --- src/basic/macro.h | 3 --- src/fundamental/macro-fundamental.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/basic/macro.h b/src/basic/macro.h index 8343528e85..7e2bc628db 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -211,9 +211,6 @@ static inline int __coverity_check_and_return__(int condition) { #define PTR_TO_UINT64(p) ((uint64_t) ((uintptr_t) (p))) #define UINT64_TO_PTR(u) ((void *) ((uintptr_t) (u))) -#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) -#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) - #define CHAR_TO_STR(x) ((char[2]) { x, 0 }) #define char_array_0(x) x[sizeof(x)-1] = 0; diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 38230c5f19..913c8b253c 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -561,3 +561,6 @@ static inline uint64_t ALIGN_OFFSET_U64(uint64_t l, uint64_t ali) { #define FOREACH_ELEMENT(i, array) \ FOREACH_ARRAY(i, array, ELEMENTSOF(array)) + +#define PTR_TO_SIZE(p) ((size_t) ((uintptr_t) (p))) +#define SIZE_TO_PTR(u) ((void *) ((uintptr_t) (u))) From ba694646eaa4a5e2f1963252a4917c5335496f7b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:17:26 +0200 Subject: [PATCH 04/36] iovec-util: make "struct iovec" and some helpers also available in EFI mode The construct is a POSIX invention, but it's just so useful, let's also define it in EFI mode, so that we can use similar constructs in EFI mode and userspace. --- src/basic/iovec-util.h | 26 +---------------- src/fundamental/iovec-util-fundamental.h | 37 ++++++++++++++++++++++++ src/fundamental/meson.build | 1 + 3 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 src/fundamental/iovec-util-fundamental.h diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index a499fef9ec..b92257cb0d 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -6,6 +6,7 @@ #include #include "alloc-util.h" +#include "iovec-util-fundamental.h" #include "macro.h" extern const struct iovec iovec_nul_byte; /* Points to a single NUL byte */ @@ -15,13 +16,6 @@ size_t iovec_total_size(const struct iovec *iovec, size_t n); bool iovec_increment(struct iovec *iovec, size_t n, size_t k); -/* This accepts both const and non-const pointers */ -#define IOVEC_MAKE(base, len) \ - (struct iovec) { \ - .iov_base = (void*) (base), \ - .iov_len = (len), \ - } - static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s) { assert(iovec); /* We don't use strlen_ptr() here, because we don't want to include string-util.h for now */ @@ -38,14 +32,6 @@ static inline struct iovec* iovec_make_string(struct iovec *iovec, const char *s .iov_len = STRLEN(s), \ } -static inline void iovec_done(struct iovec *iovec) { - /* A _cleanup_() helper that frees the iov_base in the iovec */ - assert(iovec); - - iovec->iov_base = mfree(iovec->iov_base); - iovec->iov_len = 0; -} - static inline void iovec_done_erase(struct iovec *iovec) { assert(iovec); @@ -53,16 +39,6 @@ static inline void iovec_done_erase(struct iovec *iovec) { iovec->iov_len = 0; } -static inline bool iovec_is_set(const struct iovec *iovec) { - /* Checks if the iovec points to a non-empty chunk of memory */ - return iovec && iovec->iov_len > 0 && iovec->iov_base; -} - -static inline bool iovec_is_valid(const struct iovec *iovec) { - /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */ - return !iovec || (iovec->iov_base || iovec->iov_len == 0); -} - char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); diff --git a/src/fundamental/iovec-util-fundamental.h b/src/fundamental/iovec-util-fundamental.h new file mode 100644 index 0000000000..68d5bf4ee0 --- /dev/null +++ b/src/fundamental/iovec-util-fundamental.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#if SD_BOOT +/* struct iovec is a POSIX userspace construct. Let's introduce it also in EFI mode, it's just so useful */ +struct iovec { + void *iov_base; + size_t iov_len; +}; + +static inline void free(void *p); +#endif + +/* This accepts both const and non-const pointers */ +#define IOVEC_MAKE(base, len) \ + (struct iovec) { \ + .iov_base = (void*) (base), \ + .iov_len = (len), \ + } + +static inline void iovec_done(struct iovec *iovec) { + /* A _cleanup_() helper that frees the iov_base in the iovec */ + assert(iovec); + + iovec->iov_base = mfree(iovec->iov_base); + iovec->iov_len = 0; +} + +static inline bool iovec_is_set(const struct iovec *iovec) { + /* Checks if the iovec points to a non-empty chunk of memory */ + return iovec && iovec->iov_len > 0 && iovec->iov_base; +} + +static inline bool iovec_is_valid(const struct iovec *iovec) { + /* Checks if the iovec is either NULL, empty or points to a valid bit of memory */ + return !iovec || (iovec->iov_base || iovec->iov_len == 0); +} diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build index f5f57ac3cb..3e9866ef70 100644 --- a/src/fundamental/meson.build +++ b/src/fundamental/meson.build @@ -5,6 +5,7 @@ fundamental_include = include_directories('.') fundamental_sources = files( 'bootspec-fundamental.c', 'efivars-fundamental.c', + 'iovec-util-fundamental.h', 'sha256-fundamental.c', 'string-util-fundamental.c', 'uki.c', From 9573ab8f5a1e2dfdb3542aa647868ff73ced7dd2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 15:43:28 +0200 Subject: [PATCH 05/36] util: make file_read() 64bit offset safe File offsets in UEFI are 64bit on all archs, hence let's use that typo too, and not create artificial confusion around types. --- src/boot/efi/util.c | 12 +++++++++++- src/boot/efi/util.h | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index b5c8c6306e..eb29eb2d5b 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -330,7 +330,14 @@ EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) { return EFI_SUCCESS; } -EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **ret, size_t *ret_size) { +EFI_STATUS file_read( + EFI_FILE *dir, + const char16_t *name, + uint64_t off, + size_t size, + char **ret, + size_t *ret_size) { + _cleanup_(file_closep) EFI_FILE *handle = NULL; _cleanup_free_ char *buf = NULL; EFI_STATUS err; @@ -350,6 +357,9 @@ EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t siz if (err != EFI_SUCCESS) return err; + if (info->FileSize > SIZE_MAX) + return EFI_BAD_BUFFER_SIZE; + size = info->FileSize; } diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index ceac07ca39..dc624f45ae 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -102,7 +102,7 @@ char16_t *xstr8_to_path(const char *stra); char16_t *mangle_stub_cmdline(char16_t *cmdline); EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf); -EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t size, char **content, size_t *content_size); +EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, uint64_t off, size_t size, char **content, size_t *content_size); static inline void file_closep(EFI_FILE **handle) { if (!*handle) From 4a479597218d40df1097ac882f5db9cc747d0f7f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 15:25:07 +0200 Subject: [PATCH 06/36] pe: be more careful when loading PE section list into memory Let's put a limit on how much memory we'll allocate for the section. And let's add a safety overflow check. (This is more a theoretic than a real problem, since on all PE archs NumberOfSections is 16bit only.) --- src/boot/efi/pe.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 829266b7f5..8c5e5f732d 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -119,6 +119,8 @@ typedef struct PeSectionHeader { uint32_t Characteristics; } _packed_ PeSectionHeader; +#define SECTION_TABLE_BYTES_MAX (16U * 1024U * 1024U) + static bool verify_dos(const DosFileHeader *dos) { assert(dos); return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0; @@ -309,7 +311,13 @@ EFI_STATUS pe_file_locate_sections( if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ false)) return EFI_LOAD_ERROR; - section_table_len = pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader); + DISABLE_WARNING_TYPE_LIMITS; + if ((size_t) pe.FileHeader.NumberOfSections > SIZE_MAX / sizeof(PeSectionHeader)) + return EFI_OUT_OF_RESOURCES; + REENABLE_WARNING; + section_table_len = (size_t) pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader); + if (section_table_len > SECTION_TABLE_BYTES_MAX) + return EFI_OUT_OF_RESOURCES; section_table = xmalloc(section_table_len); if (!section_table) return EFI_OUT_OF_RESOURCES; From bacc2ed0d5bb10de5d37a1df73c061247f005b59 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 17:40:32 +0200 Subject: [PATCH 07/36] pe: tighten validity checks of DOS and PE headers Let's make sure we validate that the DOS/PE header offsets are actually reasonable, and do not cause overflows when added to the base addresses. (This shouldn're really be a problem URL, since the DOS header offset is 16bit only, but let's be extra careful with this) --- src/boot/efi/pe.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 8c5e5f732d..34b01561f0 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -123,22 +123,35 @@ typedef struct PeSectionHeader { static bool verify_dos(const DosFileHeader *dos) { assert(dos); - return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0; + + DISABLE_WARNING_TYPE_LIMITS; + return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0 && + dos->ExeHeader >= sizeof(DosFileHeader) && + (size_t) dos->ExeHeader <= SIZE_MAX - sizeof(PeFileHeader); + REENABLE_WARNING; } -static bool verify_pe(const PeFileHeader *pe, bool allow_compatibility) { +static bool verify_pe( + const DosFileHeader *dos, + const PeFileHeader *pe, + bool allow_compatibility) { + + assert(dos); assert(pe); + return memcmp(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 && - (pe->FileHeader.Machine == TARGET_MACHINE_TYPE || - (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) && - pe->FileHeader.NumberOfSections > 0 && - pe->FileHeader.NumberOfSections <= MAX_SECTIONS && - IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC); + (pe->FileHeader.Machine == TARGET_MACHINE_TYPE || + (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) && + pe->FileHeader.NumberOfSections > 0 && + pe->FileHeader.NumberOfSections <= MAX_SECTIONS && + IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC) && + pe->FileHeader.SizeOfOptionalHeader < SIZE_MAX - (dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader)); } static size_t section_table_offset(const DosFileHeader *dos, const PeFileHeader *pe) { assert(dos); assert(pe); + return dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader; } @@ -220,7 +233,7 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) { return EFI_LOAD_ERROR; const PeFileHeader *pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader); - if (!verify_pe(pe, /* allow_compatibility= */ true)) + if (!verify_pe(dos, pe, /* allow_compatibility= */ true)) return EFI_LOAD_ERROR; /* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */ @@ -255,8 +268,8 @@ EFI_STATUS pe_memory_locate_sections(const void *base, const char * const sectio if (!verify_dos(dos)) return EFI_LOAD_ERROR; - pe = (const PeFileHeader *) ((uint8_t *) base + dos->ExeHeader); - if (!verify_pe(pe, /* allow_compatibility= */ false)) + pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader); + if (!verify_pe(dos, pe, /* allow_compatibility= */ false)) return EFI_LOAD_ERROR; offset = section_table_offset(dos, pe); @@ -308,7 +321,7 @@ EFI_STATUS pe_file_locate_sections( err = handle->Read(handle, &len, &pe); if (err != EFI_SUCCESS) return err; - if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ false)) + if (len != sizeof(pe) || !verify_pe(&dos, &pe, /* allow_compatibility= */ false)) return EFI_LOAD_ERROR; DISABLE_WARNING_TYPE_LIMITS; From 9e63e28aa652df483f431ab35a5934d77aab95db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 17:50:34 +0200 Subject: [PATCH 08/36] pe: use more correct section name comparison function we should only compare up to 8 chars --- src/boot/efi/pe.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 34b01561f0..cb75c2b42b 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -155,6 +155,26 @@ static size_t section_table_offset(const DosFileHeader *dos, const PeFileHeader return dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader; } +static bool pe_section_name_equal(const char *a, const char *b) { + + if (a == b) + return true; + if (!a != !b) + return false; + + /* Compares up to 8 characters of a and b i.e. the name size limit in the PE section header */ + + for (size_t i = 0; i < sizeof_field(PeSectionHeader, Name); i++) { + if (a[i] != b[i]) + return false; + + if (a[i] == 0) /* Name is shorter than 8 */ + return true; + } + + return true; +} + static void locate_sections( const PeSectionHeader section_table[], size_t n_table, @@ -172,7 +192,7 @@ static void locate_sections( const PeSectionHeader *sect = section_table + i; for (size_t j = 0; sections[j]; j++) { - if (memcmp(sect->Name, sections[j], strlen8(sections[j])) != 0) + if (!pe_section_name_equal((const char*) sect->Name, sections[j])) continue; offsets[j] = in_memory ? sect->VirtualAddress : sect->PointerToRawData; From fb974ac485c90f9887d5d21ac25d6d26d452eb3c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 24 Jun 2024 17:52:10 +0200 Subject: [PATCH 09/36] efi: introduce PeSectionVector structure, and use it for referencing PE sections --- src/boot/efi/boot.c | 38 ++++++++++------ src/boot/efi/pe.c | 107 +++++++++++++++++++++++++++++--------------- src/boot/efi/pe.h | 18 ++++++-- src/boot/efi/stub.c | 98 +++++++++++++++++++++------------------- 4 files changed, 164 insertions(+), 97 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 79de121f0d..99061a1339 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1855,23 +1855,24 @@ static void generate_boot_entry_titles(Config *config) { } static bool is_sd_boot(EFI_FILE *root_dir, const char16_t *loader_path) { - EFI_STATUS err; static const char * const sections[] = { ".sdmagic", NULL }; - size_t offset = 0, size = 0, read; _cleanup_free_ char *content = NULL; + PeSectionVector vector = {}; + EFI_STATUS err; + size_t read; assert(root_dir); assert(loader_path); - err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size); - if (err != EFI_SUCCESS || size != sizeof(SD_MAGIC)) + err = pe_file_locate_sections(root_dir, loader_path, sections, &vector); + if (err != EFI_SUCCESS || vector.size != sizeof(SD_MAGIC)) return false; - err = file_read(root_dir, loader_path, offset, size, &content, &read); - if (err != EFI_SUCCESS || size != read) + err = file_read(root_dir, loader_path, vector.file_offset, vector.size, &content, &read); + if (err != EFI_SUCCESS || vector.size != read) return false; return memcmp(content, SD_MAGIC, sizeof(SD_MAGIC)) == 0; @@ -2104,7 +2105,7 @@ static void config_load_type2_entries( _SECTION_MAX, }; - static const char * const sections[_SECTION_MAX + 1] = { + static const char * const section_names[_SECTION_MAX + 1] = { [SECTION_CMDLINE] = ".cmdline", [SECTION_OSREL] = ".osrel", NULL, @@ -2114,8 +2115,9 @@ static void config_load_type2_entries( *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; const char16_t *good_name, *good_version, *good_sort_key; _cleanup_free_ char *content = NULL; - size_t offs[_SECTION_MAX] = {}, szs[_SECTION_MAX] = {}, pos = 0; + PeSectionVector sections[_SECTION_MAX] = {}; char *line, *key, *value; + size_t pos = 0; err = readdir(linux_dir, &f, &f_size); if (err != EFI_SUCCESS || !f) @@ -2131,11 +2133,16 @@ static void config_load_type2_entries( continue; /* look for .osrel and .cmdline sections in the .efi binary */ - err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs); - if (err != EFI_SUCCESS || szs[SECTION_OSREL] == 0) + err = pe_file_locate_sections(linux_dir, f->FileName, section_names, sections); + if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL)) continue; - err = file_read(linux_dir, f->FileName, offs[SECTION_OSREL], szs[SECTION_OSREL], &content, NULL); + err = file_read(linux_dir, + f->FileName, + sections[SECTION_OSREL].file_offset, + sections[SECTION_OSREL].size, + &content, + NULL); if (err != EFI_SUCCESS) continue; @@ -2206,14 +2213,19 @@ static void config_load_type2_entries( config_add_entry(config, entry); boot_entry_parse_tries(entry, u"\\EFI\\Linux", f->FileName, u".efi"); - if (szs[SECTION_CMDLINE] == 0) + if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE)) continue; content = mfree(content); /* read the embedded cmdline file */ size_t cmdline_len; - err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, &cmdline_len); + err = file_read(linux_dir, + f->FileName, + sections[SECTION_CMDLINE].file_offset, + sections[SECTION_CMDLINE].size, + &content, + &cmdline_len); if (err == EFI_SUCCESS) { entry->options = xstrn8_to_16(content, cmdline_len); mangle_stub_cmdline(entry->options); diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index cb75c2b42b..587e3ef7b1 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -101,7 +101,7 @@ typedef struct PeOptionalHeader { } _packed_ PeOptionalHeader; typedef struct PeFileHeader { - uint8_t Magic[4]; + uint8_t Magic[4]; CoffFileHeader FileHeader; PeOptionalHeader OptionalHeader; } _packed_ PeFileHeader; @@ -175,48 +175,78 @@ static bool pe_section_name_equal(const char *a, const char *b) { return true; } -static void locate_sections( +static void pe_locate_sections( const PeSectionHeader section_table[], - size_t n_table, + size_t n_section_table, const char * const sections[], - size_t *offsets, - size_t *sizes, - bool in_memory) { + size_t validate_base, + PeSectionVector *ret_sections) { - assert(section_table); + assert(section_table || n_section_table == 0); assert(sections); - assert(offsets); - assert(sizes); + assert(ret_sections); - for (size_t i = 0; i < n_table; i++) { - const PeSectionHeader *sect = section_table + i; + /* Searches for the sections listed in 'sections[]' within the section table. Validates the resulted + * data. If 'validate_base' is non-zero also takes base offset when loaded into memory into account for + * qchecking for overflows. */ - for (size_t j = 0; sections[j]; j++) { - if (!pe_section_name_equal((const char*) sect->Name, sections[j])) + for (size_t i = 0; sections[i]; i++) + FOREACH_ARRAY(j, section_table, n_section_table) { + + if (!pe_section_name_equal((const char*) j->Name, sections[i])) continue; - offsets[j] = in_memory ? sect->VirtualAddress : sect->PointerToRawData; - sizes[j] = sect->VirtualSize; + /* Overflow check: ignore sections that are impossibly large, relative to the file + * address for the section. */ + size_t size_max = SIZE_MAX - j->PointerToRawData; + if ((size_t) j->VirtualSize > size_max) + continue; + + /* Overflow check: ignore sections that are impossibly large, given the virtual + * address for the section */ + size_max = SIZE_MAX - j->VirtualAddress; + if (j->VirtualSize > size_max) + continue; + + /* 2nd overflow check: ignore sections that are impossibly large also taking the + * loaded base into account. */ + if (validate_base != 0) { + if (validate_base > size_max) + continue; + size_max -= validate_base; + + if (j->VirtualAddress > size_max) + continue; + } + + /* At this time, the sizes and offsets have been validated. Store them away */ + ret_sections[i] = (PeSectionVector) { + .size = j->VirtualSize, + .file_offset = j->PointerToRawData, + .memory_offset = j->VirtualAddress, + }; + + /* First matching section wins, ignore the rest */ + break; } - } } static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const PeFileHeader *pe) { - size_t addr = 0, size = 0; static const char *sections[] = { ".compat", NULL }; + PeSectionVector vector = {}; /* The kernel may provide alternative PE entry points for different PE architectures. This allows * booting a 64-bit kernel on 32-bit EFI that is otherwise running on a 64-bit CPU. The locations of any * such compat entry points are located in a special PE section. */ - locate_sections((const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)), + pe_locate_sections( + (const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)), pe->FileHeader.NumberOfSections, sections, - &addr, - &size, - /*in_memory=*/true); + PTR_TO_SIZE(dos), + &vector); - if (size == 0) + if (vector.size == 0) /* not found */ return 0; typedef struct { @@ -226,6 +256,8 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const uint32_t entry_point; } _packed_ LinuxPeCompat1; + size_t addr = vector.memory_offset, size = vector.size; + while (size >= sizeof(LinuxPeCompat1) && addr % alignof(LinuxPeCompat1) == 0) { LinuxPeCompat1 *compat = (LinuxPeCompat1 *) ((uint8_t *) dos + addr); @@ -274,15 +306,18 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) { return EFI_SUCCESS; } -EFI_STATUS pe_memory_locate_sections(const void *base, const char * const sections[], size_t *addrs, size_t *sizes) { +EFI_STATUS pe_memory_locate_sections( + const void *base, + const char* const sections[], + PeSectionVector *ret_sections) { + const DosFileHeader *dos; const PeFileHeader *pe; size_t offset; assert(base); assert(sections); - assert(addrs); - assert(sizes); + assert(ret_sections); dos = (const DosFileHeader *) base; if (!verify_dos(dos)) @@ -293,12 +328,12 @@ EFI_STATUS pe_memory_locate_sections(const void *base, const char * const sectio return EFI_LOAD_ERROR; offset = section_table_offset(dos, pe); - locate_sections((PeSectionHeader *) ((uint8_t *) base + offset), + pe_locate_sections( + (const PeSectionHeader *) ((const uint8_t *) base + offset), pe->FileHeader.NumberOfSections, sections, - addrs, - sizes, - /*in_memory=*/true); + PTR_TO_SIZE(base), + ret_sections); return EFI_SUCCESS; } @@ -307,8 +342,7 @@ EFI_STATUS pe_file_locate_sections( EFI_FILE *dir, const char16_t *path, const char * const sections[], - size_t *offsets, - size_t *sizes) { + PeSectionVector *ret_sections) { _cleanup_free_ PeSectionHeader *section_table = NULL; _cleanup_(file_closep) EFI_FILE *handle = NULL; DosFileHeader dos; @@ -319,8 +353,7 @@ EFI_STATUS pe_file_locate_sections( assert(dir); assert(path); assert(sections); - assert(offsets); - assert(sizes); + assert(ret_sections); err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL); if (err != EFI_SUCCESS) @@ -366,8 +399,12 @@ EFI_STATUS pe_file_locate_sections( if (len != section_table_len) return EFI_LOAD_ERROR; - locate_sections(section_table, pe.FileHeader.NumberOfSections, - sections, offsets, sizes, /*in_memory=*/false); + pe_locate_sections( + section_table, + pe.FileHeader.NumberOfSections, + sections, + /* validate_base= */ 0, /* don't validate base */ + ret_sections); return EFI_SUCCESS; } diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h index 7e2258fceb..bc6d74beeb 100644 --- a/src/boot/efi/pe.h +++ b/src/boot/efi/pe.h @@ -3,17 +3,27 @@ #include "efi.h" +/* This is a subset of the full PE section header structure, with validated values, and without + * the noise. */ +typedef struct PeSectionVector { + size_t size; + size_t memory_offset; /* Offset in memory, relative to base address */ + uint64_t file_offset; /* Offset on disk, relative to beginning of file */ +} PeSectionVector; + +static inline bool PE_SECTION_VECTOR_IS_SET(const PeSectionVector *v) { + return v && v->size != 0; +} + EFI_STATUS pe_memory_locate_sections( const void *base, const char * const sections[], - size_t *addrs, - size_t *sizes); + PeSectionVector *ret_sections); EFI_STATUS pe_file_locate_sections( EFI_FILE *dir, const char16_t *path, const char * const sections[], - size_t *offsets, - size_t *sizes); + PeSectionVector *ret_sections); EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index f5d2b1d4c5..1375b88436 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -413,7 +413,7 @@ static EFI_STATUS load_addons( sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16); for (size_t i = 0; i < n_items; i++) { - size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; + PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL; _cleanup_(unload_imagep) EFI_HANDLE addon = NULL; EFI_LOADED_IMAGE_PROTOCOL *loaded_addon = NULL; @@ -441,9 +441,10 @@ static EFI_STATUS load_addons( if (err != EFI_SUCCESS) return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]); - err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs); + err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, sections); if (err != EFI_SUCCESS || - (szs[UNIFIED_SECTION_CMDLINE] == 0 && szs[UNIFIED_SECTION_DTB] == 0)) { + (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE) && + !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB))) { if (err == EFI_SUCCESS) err = EFI_NOT_FOUND; log_error_status(err, @@ -453,38 +454,38 @@ static EFI_STATUS load_addons( } /* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */ - if (szs[UNIFIED_SECTION_LINUX] > 0) { + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { log_error_status(EFI_INVALID_PARAMETER, "%ls is a UKI, not an addon, ignoring: %m", items[i]); continue; } /* Also enforce that, in case it is specified, .uname matches as a quick way to allow * enforcing compatibility with a specific UKI only */ - if (uname && szs[UNIFIED_SECTION_UNAME] > 0 && + if (uname && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME) && !strneq8(uname, - (char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_UNAME], - szs[UNIFIED_SECTION_UNAME])) { + (char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, + sections[UNIFIED_SECTION_UNAME].size)) { log_error(".uname mismatch between %ls and UKI, ignoring", items[i]); continue; } - if (ret_cmdline && szs[UNIFIED_SECTION_CMDLINE] > 0) { + if (ret_cmdline && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), - *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE], - szs[UNIFIED_SECTION_CMDLINE]); + *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, + sections[UNIFIED_SECTION_CMDLINE].size); cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); } - if (ret_dt_bases && szs[UNIFIED_SECTION_DTB] > 0) { + if (ret_dt_bases && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { dt_sizes = xrealloc(dt_sizes, n_dt * sizeof(size_t), (n_dt + 1) * sizeof(size_t)); - dt_sizes[n_dt] = szs[UNIFIED_SECTION_DTB]; + dt_sizes[n_dt] = sections[UNIFIED_SECTION_DTB].size; dt_bases = xrealloc(dt_bases, n_dt * sizeof(void *), (n_dt + 1) * sizeof(void *)); - dt_bases[n_dt] = xmemdup((uint8_t*)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_DTB], + dt_bases[n_dt] = xmemdup((uint8_t*)loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, dt_sizes[n_dt]); dt_filenames = xrealloc(dt_filenames, @@ -515,11 +516,11 @@ static EFI_STATUS run(EFI_HANDLE image) { void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL; _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL; - size_t linux_size, initrd_size, ucode_size, dt_size, n_dts_addons_global = 0, n_dts_addons_uki = 0; - EFI_PHYSICAL_ADDRESS linux_base, initrd_base, ucode_base, dt_base; + size_t linux_size, initrd_size = 0, ucode_size = 0, dt_size = 0, n_dts_addons_global = 0, n_dts_addons_uki = 0; + EFI_PHYSICAL_ADDRESS linux_base, initrd_base = 0, ucode_base = 0, dt_base = 0; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; - size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {}; + PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL; int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; _cleanup_free_ char *uname = NULL; @@ -541,8 +542,8 @@ static EFI_STATUS run(EFI_HANDLE image) { (void) process_random_seed(esp_dir); } - err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs); - if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) { + err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, sections); + if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { if (err == EFI_SUCCESS) err = EFI_NOT_FOUND; return log_error_status(err, "Unable to locate embedded .linux section: %m"); @@ -553,9 +554,9 @@ static EFI_STATUS run(EFI_HANDLE image) { CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free); CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free); - if (szs[UNIFIED_SECTION_UNAME] > 0) - uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME], - szs[UNIFIED_SECTION_UNAME]); + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME)) + uname = xstrndup8((char *)loaded_image->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, + sections[UNIFIED_SECTION_UNAME].size); /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ @@ -597,7 +598,7 @@ static EFI_STATUS run(EFI_HANDLE image) { if (!unified_section_measure(section)) /* shall not measure? */ continue; - if (szs[section] == 0) /* not found */ + if (!PE_SECTION_VECTOR_IS_SET(sections + section)) /* not found */ continue; /* First measure the name of the section */ @@ -614,11 +615,10 @@ static EFI_STATUS run(EFI_HANDLE image) { m = false; (void) tpm_log_ipl_event_ascii( TPM2_PCR_KERNEL_BOOT, - POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[section], - szs[section], + POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[section].memory_offset, + sections[section].size, unified_sections[section], &m); - combine_measured_flag(§ions_measured, m); } @@ -628,9 +628,10 @@ static EFI_STATUS run(EFI_HANDLE image) { (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0); /* Show splash screen as early as possible */ - graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]); + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_SPLASH)) + graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size); - if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) { + if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), &cmdline)) { /* Let's measure the passed kernel command line into the TPM. Note that this possibly * duplicates what we already did in the boot menu, if that was already used. However, since * we want the boot menu to support an EFI binary, and want to this stub to be usable from @@ -638,10 +639,10 @@ static EFI_STATUS run(EFI_HANDLE image) { m = false; (void) tpm_log_load_options(cmdline, &m); combine_measured_flag(¶meters_measured, m); - } else if (szs[UNIFIED_SECTION_CMDLINE] > 0) { + } else if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { cmdline = xstrn8_to_16( - (char *) loaded_image->ImageBase + addrs[UNIFIED_SECTION_CMDLINE], - szs[UNIFIED_SECTION_CMDLINE]); + (char *) loaded_image->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, + sections[UNIFIED_SECTION_CMDLINE].size); mangle_stub_cmdline(cmdline); } @@ -727,8 +728,11 @@ static EFI_STATUS run(EFI_HANDLE image) { &m) == EFI_SUCCESS) combine_measured_flag(&confext_measured, m); - dt_size = szs[UNIFIED_SECTION_DTB]; - dt_base = dt_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_DTB] : 0; + + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { + dt_size = sections[UNIFIED_SECTION_DTB].size; + dt_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_DTB].memory_offset; + } /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */ if (dt_size > 0) { @@ -767,10 +771,10 @@ static EFI_STATUS run(EFI_HANDLE image) { * is not measured, neither as raw section (see above), nor as cpio (here), because it is the * signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't * itself be input for PCR measurements. */ - if (szs[UNIFIED_SECTION_PCRSIG] > 0) + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRSIG)) (void) pack_cpio_literal( - (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRSIG], - szs[UNIFIED_SECTION_PCRSIG], + (uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRSIG].memory_offset, + sections[UNIFIED_SECTION_PCRSIG].size, ".extra", u"tpm2-pcr-signature.json", /* dir_mode= */ 0555, @@ -785,10 +789,10 @@ static EFI_STATUS run(EFI_HANDLE image) { * a cpio and also pass it to the kernel, so that it can be read from * /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the * cpio. */ - if (szs[UNIFIED_SECTION_PCRPKEY] > 0) + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRPKEY)) (void) pack_cpio_literal( - (uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRPKEY], - szs[UNIFIED_SECTION_PCRPKEY], + (uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRPKEY].memory_offset, + sections[UNIFIED_SECTION_PCRPKEY].size, ".extra", u"tpm2-pcr-public-key.pem", /* dir_mode= */ 0555, @@ -799,14 +803,18 @@ static EFI_STATUS run(EFI_HANDLE image) { &pcrpkey_initrd_size, /* ret_measured= */ NULL); - linux_size = szs[UNIFIED_SECTION_LINUX]; - linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_LINUX]; + linux_size = sections[UNIFIED_SECTION_LINUX].size; + linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_LINUX].memory_offset; - initrd_size = szs[UNIFIED_SECTION_INITRD]; - initrd_base = initrd_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_INITRD] : 0; + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) { + initrd_size = sections[UNIFIED_SECTION_INITRD].size; + initrd_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_INITRD].memory_offset; + } - ucode_size = szs[UNIFIED_SECTION_UCODE]; - ucode_base = ucode_size != 0 ? POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + addrs[UNIFIED_SECTION_UCODE] : 0; + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) { + ucode_size = sections[UNIFIED_SECTION_UCODE].size; + ucode_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_UCODE].memory_offset; + } _cleanup_pages_ Pages initrd_pages = {}; if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) { @@ -857,4 +865,4 @@ static EFI_STATUS run(EFI_HANDLE image) { return err; } -DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /*wait_for_debugger=*/false); +DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /* wait_for_debugger= */ false); From 201e0d53bdd437dd6c1de81a51503f82442bf27d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Jun 2024 22:45:57 +0200 Subject: [PATCH 10/36] stub: split out random seed part out of run() --- src/boot/efi/stub.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 1375b88436..eaca103801 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -510,6 +510,32 @@ static EFI_STATUS load_addons( return EFI_SUCCESS; } +static void refresh_random_seed(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { + EFI_STATUS err; + + assert(loaded_image); + + /* Handle case, where bootloader doesn't support DeviceHandle. */ + if (!loaded_image->DeviceHandle) + return; + + uint64_t loader_features = 0; + err = efivar_get_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", &loader_features); + if (err != EFI_SUCCESS) + return; + + /* Don't measure again, if sd-boot already initialized the random seed */ + if (!FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED)) + return; + + _cleanup_(file_closep) EFI_FILE *esp_dir = NULL; + err = partition_open(MAKE_GUID_PTR(ESP), loaded_image->DeviceHandle, NULL, &esp_dir); + if (err != EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */ + return; + + (void) process_random_seed(esp_dir); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; @@ -525,22 +551,13 @@ static EFI_STATUS run(EFI_HANDLE image) { int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; _cleanup_free_ char *uname = NULL; bool m; - uint64_t loader_features = 0; EFI_STATUS err; err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image); if (err != EFI_SUCCESS) return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m"); - if (loaded_image->DeviceHandle && /* Handle case, where bootloader doesn't support DeviceHandle. */ - (efivar_get_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", &loader_features) != EFI_SUCCESS || - !FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED))) { - _cleanup_(file_closep) EFI_FILE *esp_dir = NULL; - - err = partition_open(MAKE_GUID_PTR(ESP), loaded_image->DeviceHandle, NULL, &esp_dir); - if (err == EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */ - (void) process_random_seed(esp_dir); - } + refresh_random_seed(loaded_image); err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, sections); if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { From cd6fe7aa7f95958842b86debcaa351ef627df03f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 25 Jun 2024 22:49:24 +0200 Subject: [PATCH 11/36] stub: split out code that measures our own PE sections --- src/boot/efi/stub.c | 74 ++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 31 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index eaca103801..24e7bb3191 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -536,6 +536,48 @@ static void refresh_random_seed(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { (void) process_random_seed(esp_dir); } +static void measure_sections( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX], + int *sections_measured) { + + assert(loaded_image); + assert(sections); + assert(sections_measured); + + /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written + * into so far), so that we have one PCR that we can nicely write policies against because it + * contains all static data of this image, and thus can be easily be pre-calculated. */ + for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) { + + if (!unified_section_measure(section)) /* shall not measure? */ + continue; + + if (!PE_SECTION_VECTOR_IS_SET(sections + section)) /* not found */ + continue; + + /* First measure the name of the section */ + bool m = false; + (void) tpm_log_ipl_event_ascii( + TPM2_PCR_KERNEL_BOOT, + POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]), + strsize8(unified_sections[section]), /* including NUL byte */ + unified_sections[section], + &m); + combine_measured_flag(sections_measured, m); + + /* Then measure the data of the section */ + m = false; + (void) tpm_log_ipl_event_ascii( + TPM2_PCR_KERNEL_BOOT, + POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[section].memory_offset, + sections[section].size, + unified_sections[section], + &m); + combine_measured_flag(sections_measured, m); + } +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; @@ -607,37 +649,7 @@ static EFI_STATUS run(EFI_HANDLE image) { log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); } - /* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written - * into so far), so that we have one PCR that we can nicely write policies against because it - * contains all static data of this image, and thus can be easily be pre-calculated. */ - for (UnifiedSection section = 0; section < _UNIFIED_SECTION_MAX; section++) { - - if (!unified_section_measure(section)) /* shall not measure? */ - continue; - - if (!PE_SECTION_VECTOR_IS_SET(sections + section)) /* not found */ - continue; - - /* First measure the name of the section */ - m = false; - (void) tpm_log_ipl_event_ascii( - TPM2_PCR_KERNEL_BOOT, - POINTER_TO_PHYSICAL_ADDRESS(unified_sections[section]), - strsize8(unified_sections[section]), /* including NUL byte */ - unified_sections[section], - &m); - combine_measured_flag(§ions_measured, m); - - /* Then measure the data of the section */ - m = false; - (void) tpm_log_ipl_event_ascii( - TPM2_PCR_KERNEL_BOOT, - POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[section].memory_offset, - sections[section].size, - unified_sections[section], - &m); - combine_measured_flag(§ions_measured, m); - } + measure_sections(loaded_image, sections, §ions_measured); /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode * in it which PCR was used. */ From 4a033b0a11a1e964c4d1ec776e955103a037b12b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 09:20:54 +0200 Subject: [PATCH 12/36] stub: split out code that appends smbios command line --- src/boot/efi/stub.c | 49 ++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 24e7bb3191..8c3c7d5422 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -578,6 +578,38 @@ static void measure_sections( } } +static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameters_measured) { + assert(cmdline); + assert(parameters_measured); + + /* SMBIOS OEM Strings data is controlled by the host admin and not covered by the VM attestation, so + * MUST NOT be trusted when in a confidential VM */ + if (is_confidential_vm()) + return; + + const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); + if (!extra) + return; + + _cleanup_free_ char16_t *extra16 = xstr8_to_16(extra); + mangle_stub_cmdline(extra16); + if (isempty(extra16)) + return; + + /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific PCR12, as + * firmware-owned PCRs are very difficult to use as they'll contain unpredictable measurements that + * are not under control of the machine owner. */ + bool m = false; + (void) tpm_log_load_options(extra16, &m); + combine_measured_flag(parameters_measured, m); + + _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline); + if (isempty(tmp)) + *cmdline = TAKE_PTR(extra16); + else + *cmdline = xasprintf("%ls %ls", tmp, extra16); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; @@ -682,22 +714,7 @@ static EFI_STATUS run(EFI_HANDLE image) { cmdline_append_and_measure_addons(cmdline_addons_global, cmdline_addons_uki, &cmdline, &m); combine_measured_flag(¶meters_measured, m); - /* SMBIOS OEM Strings data is controlled by the host admin and not covered - * by the VM attestation, so MUST NOT be trusted when in a confidential VM */ - if (!is_confidential_vm()) { - const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra"); - if (extra) { - _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra); - cmdline = xasprintf("%ls %ls", tmp, extra16); - - /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific - * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable - * measurements that are not under control of the machine owner. */ - m = false; - (void) tpm_log_load_options(extra16, &m); - combine_measured_flag(¶meters_measured, m); - } - } + cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); export_variables(loaded_image); From 79d076be371139ac2bd222b99b4299da5ee07312 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 09:27:28 +0200 Subject: [PATCH 13/36] stub: split out code that finds the uname among PE sections --- src/boot/efi/stub.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 8c3c7d5422..291965766c 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -610,6 +610,24 @@ static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameter *cmdline = xasprintf("%ls %ls", tmp, extra16); } +static void lookup_uname( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX], + char **ret_uname) { + + assert(loaded_image); + assert(sections); + assert(ret_uname); + + if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME)) { + *ret_uname = NULL; + return; + } + + *ret_uname = xstrndup8((char *)loaded_image->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, + sections[UNIFIED_SECTION_UNAME].size); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; @@ -645,9 +663,7 @@ static EFI_STATUS run(EFI_HANDLE image) { CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free); CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME)) - uname = xstrndup8((char *)loaded_image->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, - sections[UNIFIED_SECTION_UNAME].size); + lookup_uname(loaded_image, sections, &uname); /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ From ac32323271305252935469723e460139a65ed378 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:20:26 +0200 Subject: [PATCH 14/36] stub: rework initrd handling around "struct iovec" Let's maintain an array of "struct iovec" for the initrds. It becomes a ton easier and shorter to process/combine the various initrds then. --- src/boot/efi/cpio.c | 19 ++--- src/boot/efi/cpio.h | 7 +- src/boot/efi/stub.c | 177 +++++++++++++++++++++++--------------------- 3 files changed, 100 insertions(+), 103 deletions(-) diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index a3c98c7b92..ba439740f7 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -311,8 +311,7 @@ EFI_STATUS pack_cpio( uint32_t access_mode, uint32_t tpm_pcr, const char16_t *tpm_description, - void **ret_buffer, - size_t *ret_buffer_size, + struct iovec *ret_buffer, bool *ret_measured) { _cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL; @@ -327,7 +326,6 @@ EFI_STATUS pack_cpio( assert(loaded_image); assert(target_dir_prefix); assert(ret_buffer); - assert(ret_buffer_size); if (!loaded_image->DeviceHandle) goto nothing; @@ -439,14 +437,11 @@ EFI_STATUS pack_cpio( tpm_pcr, tpm_description); - *ret_buffer = TAKE_PTR(buffer); - *ret_buffer_size = buffer_size; - + *ret_buffer = IOVEC_MAKE(TAKE_PTR(buffer), buffer_size); return EFI_SUCCESS; nothing: - *ret_buffer = NULL; - *ret_buffer_size = 0; + *ret_buffer = (struct iovec) {}; if (ret_measured) *ret_measured = false; @@ -463,8 +458,7 @@ EFI_STATUS pack_cpio_literal( uint32_t access_mode, uint32_t tpm_pcr, const char16_t *tpm_description, - void **ret_buffer, - size_t *ret_buffer_size, + struct iovec *ret_buffer, bool *ret_measured) { uint32_t inode = 1; /* inode counter, so that each item gets a new inode */ @@ -476,7 +470,6 @@ EFI_STATUS pack_cpio_literal( assert(target_dir_prefix); assert(target_filename); assert(ret_buffer); - assert(ret_buffer_size); /* Generate the leading directory inodes right before adding the first files, to the * archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */ @@ -508,8 +501,6 @@ EFI_STATUS pack_cpio_literal( tpm_pcr, tpm_description); - *ret_buffer = TAKE_PTR(buffer); - *ret_buffer_size = buffer_size; - + *ret_buffer = IOVEC_MAKE(TAKE_PTR(buffer), buffer_size); return EFI_SUCCESS; } diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h index 9d14fa15a2..660372c0b7 100644 --- a/src/boot/efi/cpio.h +++ b/src/boot/efi/cpio.h @@ -2,6 +2,7 @@ #pragma once #include "efi.h" +#include "iovec-util-fundamental.h" #include "proto/loaded-image.h" EFI_STATUS pack_cpio( @@ -14,8 +15,7 @@ EFI_STATUS pack_cpio( uint32_t access_mode, uint32_t tpm_pcr, const char16_t *tpm_description, - void **ret_buffer, - size_t *ret_buffer_size, + struct iovec *ret_buffer, bool *ret_measured); EFI_STATUS pack_cpio_literal( @@ -27,6 +27,5 @@ EFI_STATUS pack_cpio_literal( uint32_t access_mode, uint32_t tpm_pcr, const char16_t *tpm_description, - void **ret_buffer, - size_t *ret_buffer_size, + struct iovec *ret_buffer, bool *ret_measured); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 291965766c..f3abbe8921 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -4,6 +4,7 @@ #include "device-path-util.h" #include "devicetree.h" #include "graphics.h" +#include "iovec-util-fundamental.h" #include "linux.h" #include "measure.h" #include "memory-util-fundamental.h" @@ -21,6 +22,23 @@ #include "version.h" #include "vmm.h" +/* The list of initrds we combine into one, in the order we want to merge them */ +enum { + /* The first two are part of the PE binary */ + INITRD_UCODE, + INITRD_BASE, + + /* The rest are dynamically generated, and hence in dynamic memory */ + _INITRD_DYNAMIC_FIRST, + INITRD_CREDENTIAL = _INITRD_DYNAMIC_FIRST, + INITRD_GLOBAL_CREDENTIAL, + INITRD_SYSEXT, + INITRD_CONFEXT, + INITRD_PCRSIG, + INITRD_PCRPKEY, + _INITRD_MAX, +}; + /* magic string to find in the binary image */ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION " ####"); @@ -47,21 +65,19 @@ static void combine_measured_flag(int *value, int measured) { /* Combine initrds by concatenation in memory */ static EFI_STATUS combine_initrds( - const void * const initrds[], const size_t initrd_sizes[], size_t n_initrds, - Pages *ret_initr_pages, size_t *ret_initrd_size) { + struct iovec initrds[], size_t n_initrds, + Pages *ret_initrd_pages, size_t *ret_initrd_size) { size_t n = 0; - assert(ret_initr_pages); + assert(initrds || n_initrds == 0); + assert(ret_initrd_pages); assert(ret_initrd_size); - for (size_t i = 0; i < n_initrds; i++) { - if (!initrds[i]) - continue; + FOREACH_ARRAY(i, initrds, n_initrds) { + /* some initrds (the ones from UKI sections) need padding, pad all to be safe */ - /* some initrds (the ones from UKI sections) need padding, - * pad all to be safe */ - size_t initrd_size = ALIGN4(initrd_sizes[i]); + size_t initrd_size = ALIGN4(i->iov_len); if (n > SIZE_MAX - initrd_size) return EFI_OUT_OF_RESOURCES; @@ -74,24 +90,22 @@ static EFI_STATUS combine_initrds( EFI_SIZE_TO_PAGES(n), UINT32_MAX /* Below 4G boundary. */); uint8_t *p = PHYSICAL_ADDRESS_TO_POINTER(pages.addr); - for (size_t i = 0; i < n_initrds; i++) { - if (!initrds[i]) - continue; - + FOREACH_ARRAY(i, initrds, n_initrds) { size_t pad; - p = mempcpy(p, initrds[i], initrd_sizes[i]); + p = mempcpy(p, i->iov_base, i->iov_len); - pad = ALIGN4(initrd_sizes[i]) - initrd_sizes[i]; - if (pad > 0) { - memzero(p, pad); - p += pad; - } + pad = ALIGN4(i->iov_len) - i->iov_len; + if (pad == 0) + continue; + + memzero(p, pad); + p += pad; } assert(PHYSICAL_ADDRESS_TO_POINTER(pages.addr + n) == p); - *ret_initr_pages = pages; + *ret_initrd_pages = pages; *ret_initrd_size = n; pages.n_pages = 0; @@ -628,14 +642,39 @@ static void lookup_uname( sections[UNIFIED_SECTION_UNAME].size); } +static void initrds_free(struct iovec (*initrds)[_INITRD_MAX]) { + assert(initrds); + + /* Free the dynamic initrds, but leave the non-dynamic ones around */ + + for (size_t i = _INITRD_DYNAMIC_FIRST; i < _INITRD_MAX; i++) + iovec_done((*initrds) + i); +} + +static bool initrds_need_combine(struct iovec initrds[static _INITRD_MAX]) { + assert(initrds); + + /* Returns true if we have any initrds set that aren't the base initrd. In that case we need to + * merge, otherwise we can pass the embedded initrd as is */ + + for (size_t i = 0; i < _INITRD_MAX; i++) { + if (i == INITRD_BASE) + continue; + + if (iovec_is_set(initrds + i)) + return true; + } + + return false; +} + static EFI_STATUS run(EFI_HANDLE image) { - _cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *confext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL; - size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, confext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0; + _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL; _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL; - size_t linux_size, initrd_size = 0, ucode_size = 0, dt_size = 0, n_dts_addons_global = 0, n_dts_addons_uki = 0; - EFI_PHYSICAL_ADDRESS linux_base, initrd_base = 0, ucode_base = 0, dt_base = 0; + size_t dt_size = 0, n_dts_addons_global = 0, n_dts_addons_uki = 0; + EFI_PHYSICAL_ADDRESS dt_base = 0; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; @@ -743,8 +782,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0400, /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, u"Credentials initrd", - &credential_initrd, - &credential_initrd_size, + initrds + INITRD_CREDENTIAL, &m) == EFI_SUCCESS) combine_measured_flag(¶meters_measured, m); @@ -757,8 +795,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0400, /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, u"Global credentials initrd", - &global_credential_initrd, - &global_credential_initrd_size, + initrds + INITRD_GLOBAL_CREDENTIAL, &m) == EFI_SUCCESS) combine_measured_flag(¶meters_measured, m); @@ -771,8 +808,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0444, /* tpm_pcr= */ TPM2_PCR_SYSEXTS, u"System extension initrd", - &sysext_initrd, - &sysext_initrd_size, + initrds + INITRD_CONFEXT, &m) == EFI_SUCCESS) combine_measured_flag(&sysext_measured, m); @@ -785,12 +821,10 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0444, /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, u"Configuration extension initrd", - &confext_initrd, - &confext_initrd_size, + initrds + INITRD_SYSEXT, &m) == EFI_SUCCESS) combine_measured_flag(&confext_measured, m); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { dt_size = sections[UNIFIED_SECTION_DTB].size; dt_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_DTB].memory_offset; @@ -843,8 +877,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0444, /* tpm_pcr= */ UINT32_MAX, /* tpm_description= */ NULL, - &pcrsig_initrd, - &pcrsig_initrd_size, + initrds + INITRD_PCRSIG, /* ret_measured= */ NULL); /* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in @@ -861,68 +894,42 @@ static EFI_STATUS run(EFI_HANDLE image) { /* access_mode= */ 0444, /* tpm_pcr= */ UINT32_MAX, /* tpm_description= */ NULL, - &pcrpkey_initrd, - &pcrpkey_initrd_size, + initrds + INITRD_PCRPKEY, /* ret_measured= */ NULL); - linux_size = sections[UNIFIED_SECTION_LINUX].size; - linux_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_LINUX].memory_offset; + struct iovec kernel = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, + sections[UNIFIED_SECTION_LINUX].size); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) { - initrd_size = sections[UNIFIED_SECTION_INITRD].size; - initrd_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_INITRD].memory_offset; - } + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) + initrds[INITRD_BASE] = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset, + sections[UNIFIED_SECTION_INITRD].size); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) { - ucode_size = sections[UNIFIED_SECTION_UCODE].size; - ucode_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_UCODE].memory_offset; - } + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) + initrds[INITRD_UCODE] = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_UCODE].memory_offset, + sections[UNIFIED_SECTION_UCODE].size); _cleanup_pages_ Pages initrd_pages = {}; - if (ucode_base || credential_initrd || global_credential_initrd || sysext_initrd || confext_initrd || pcrsig_initrd || pcrpkey_initrd) { - /* If we have generated initrds dynamically or there is a microcode initrd, combine them with the built-in initrd. */ - err = combine_initrds( - (const void*const[]) { - /* Microcode must always be first as kernel only scans uncompressed cpios - * and later initrds might be compressed. */ - PHYSICAL_ADDRESS_TO_POINTER(ucode_base), - PHYSICAL_ADDRESS_TO_POINTER(initrd_base), - credential_initrd, - global_credential_initrd, - sysext_initrd, - confext_initrd, - pcrsig_initrd, - pcrpkey_initrd, - }, - (const size_t[]) { - ucode_size, - initrd_size, - credential_initrd_size, - global_credential_initrd_size, - sysext_initrd_size, - confext_initrd_size, - pcrsig_initrd_size, - pcrpkey_initrd_size, - }, - 8, - &initrd_pages, &initrd_size); + struct iovec final_initrd; + if (initrds_need_combine(initrds)) { + /* If we have generated initrds dynamically or there is a microcode initrd, combine them with + * the built-in initrd. */ + err = combine_initrds(initrds, _INITRD_MAX, &initrd_pages, &final_initrd.iov_len); if (err != EFI_SUCCESS) return err; - initrd_base = initrd_pages.addr; + final_initrd.iov_base = PHYSICAL_ADDRESS_TO_POINTER(initrd_pages.addr); - /* Given these might be large let's free them explicitly, quickly. */ - credential_initrd = mfree(credential_initrd); - global_credential_initrd = mfree(global_credential_initrd); - sysext_initrd = mfree(sysext_initrd); - confext_initrd = mfree(confext_initrd); - pcrsig_initrd = mfree(pcrsig_initrd); - pcrpkey_initrd = mfree(pcrpkey_initrd); - } + /* Given these might be large let's free them explicitly before we pass control to Linux */ + initrds_free(&initrds); + } else + final_initrd = initrds[INITRD_BASE]; err = linux_exec(image, cmdline, - PHYSICAL_ADDRESS_TO_POINTER(linux_base), linux_size, - PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size); + kernel.iov_base, kernel.iov_len, + final_initrd.iov_base, final_initrd.iov_len); graphics_mode(false); return err; } From 868219655a9aee05289676b2c46a69329d57079f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:25:25 +0200 Subject: [PATCH 15/36] stub: split out calls that generate sidecar initrds --- src/boot/efi/stub.c | 120 +++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 51 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index f3abbe8921..4ae4b888b8 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -668,6 +668,74 @@ static bool initrds_need_combine(struct iovec initrds[static _INITRD_MAX]) { return false; } +static void generate_sidecar_initrds( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + struct iovec initrds[static _INITRD_MAX], + int *parameters_measured, + int *sysext_measured, + int *confext_measured) { + + bool m; + + assert(loaded_image); + assert(initrds); + assert(parameters_measured); + assert(sysext_measured); + assert(confext_measured); + + if (pack_cpio(loaded_image, + /* dropin_dir= */ NULL, + u".cred", + /* exclude_suffix= */ NULL, + ".extra/credentials", + /* dir_mode= */ 0500, + /* access_mode= */ 0400, + /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, + u"Credentials initrd", + initrds + INITRD_CREDENTIAL, + &m) == EFI_SUCCESS) + combine_measured_flag(parameters_measured, m); + + if (pack_cpio(loaded_image, + u"\\loader\\credentials", + u".cred", + /* exclude_suffix= */ NULL, + ".extra/global_credentials", + /* dir_mode= */ 0500, + /* access_mode= */ 0400, + /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, + u"Global credentials initrd", + initrds + INITRD_GLOBAL_CREDENTIAL, + &m) == EFI_SUCCESS) + combine_measured_flag(parameters_measured, m); + + if (pack_cpio(loaded_image, + /* dropin_dir= */ NULL, + u".raw", /* ideally we'd pick up only *.sysext.raw here, but for compat we pick up *.raw instead … */ + u".confext.raw", /* … but then exclude *.confext.raw again */ + ".extra/sysext", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ TPM2_PCR_SYSEXTS, + u"System extension initrd", + initrds + INITRD_CONFEXT, + &m) == EFI_SUCCESS) + combine_measured_flag(sysext_measured, m); + + if (pack_cpio(loaded_image, + /* dropin_dir= */ NULL, + u".confext.raw", + /* exclude_suffix= */ NULL, + ".extra/confext", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, + u"Configuration extension initrd", + initrds + INITRD_SYSEXT, + &m) == EFI_SUCCESS) + combine_measured_flag(confext_measured, m); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; @@ -773,57 +841,7 @@ static EFI_STATUS run(EFI_HANDLE image) { export_variables(loaded_image); - if (pack_cpio(loaded_image, - /* dropin_dir= */ NULL, - u".cred", - /* exclude_suffix= */ NULL, - ".extra/credentials", - /* dir_mode= */ 0500, - /* access_mode= */ 0400, - /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, - u"Credentials initrd", - initrds + INITRD_CREDENTIAL, - &m) == EFI_SUCCESS) - combine_measured_flag(¶meters_measured, m); - - if (pack_cpio(loaded_image, - u"\\loader\\credentials", - u".cred", - /* exclude_suffix= */ NULL, - ".extra/global_credentials", - /* dir_mode= */ 0500, - /* access_mode= */ 0400, - /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, - u"Global credentials initrd", - initrds + INITRD_GLOBAL_CREDENTIAL, - &m) == EFI_SUCCESS) - combine_measured_flag(¶meters_measured, m); - - if (pack_cpio(loaded_image, - /* dropin_dir= */ NULL, - u".raw", /* ideally we'd pick up only *.sysext.raw here, but for compat we pick up *.raw instead … */ - u".confext.raw", /* … but then exclude *.confext.raw again */ - ".extra/sysext", - /* dir_mode= */ 0555, - /* access_mode= */ 0444, - /* tpm_pcr= */ TPM2_PCR_SYSEXTS, - u"System extension initrd", - initrds + INITRD_CONFEXT, - &m) == EFI_SUCCESS) - combine_measured_flag(&sysext_measured, m); - - if (pack_cpio(loaded_image, - /* dropin_dir= */ NULL, - u".confext.raw", - /* exclude_suffix= */ NULL, - ".extra/confext", - /* dir_mode= */ 0555, - /* access_mode= */ 0444, - /* tpm_pcr= */ TPM2_PCR_KERNEL_CONFIG, - u"Configuration extension initrd", - initrds + INITRD_SYSEXT, - &m) == EFI_SUCCESS) - combine_measured_flag(&confext_measured, m); + generate_sidecar_initrds(loaded_image, initrds, ¶meters_measured, &sysext_measured, &confext_measured); if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { dt_size = sections[UNIFIED_SECTION_DTB].size; From b199c0f26ef010c16655e53dfd48c8e4272da2ec Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:31:23 +0200 Subject: [PATCH 16/36] stub: split out code that generates embedded initrds --- src/boot/efi/stub.c | 79 ++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 4ae4b888b8..eb3e905f3c 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -736,6 +736,50 @@ static void generate_sidecar_initrds( combine_measured_flag(confext_measured, m); } +static void generate_embedded_initrds( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + PeSectionVector sections[_UNIFIED_SECTION_MAX], + struct iovec initrds[static _INITRD_MAX]) { + + assert(loaded_image); + assert(initrds); + + /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it + * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section + * is not measured, neither as raw section (see above), nor as cpio (here), because it is the + * signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't + * itself be input for PCR measurements. */ + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRSIG)) + (void) pack_cpio_literal( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRSIG].memory_offset, + sections[UNIFIED_SECTION_PCRSIG].size, + ".extra", + u"tpm2-pcr-signature.json", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ UINT32_MAX, + /* tpm_description= */ NULL, + initrds + INITRD_PCRSIG, + /* ret_measured= */ NULL); + + /* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in + * a cpio and also pass it to the kernel, so that it can be read from + * /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the + * cpio. */ + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRPKEY)) + (void) pack_cpio_literal( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRPKEY].memory_offset, + sections[UNIFIED_SECTION_PCRPKEY].size, + ".extra", + u"tpm2-pcr-public-key.pem", + /* dir_mode= */ 0555, + /* access_mode= */ 0444, + /* tpm_pcr= */ UINT32_MAX, + /* tpm_description= */ NULL, + initrds + INITRD_PCRPKEY, + /* ret_measured= */ NULL); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; @@ -880,40 +924,7 @@ static EFI_STATUS run(EFI_HANDLE image) { if (confext_measured > 0) (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); - /* If the PCR signature was embedded in the PE image, then let's wrap it in a cpio and also pass it - * to the kernel, so that it can be read from /.extra/tpm2-pcr-signature.json. Note that this section - * is not measured, neither as raw section (see above), nor as cpio (here), because it is the - * signature of expected PCR values, i.e. its input are PCR measurements, and hence it shouldn't - * itself be input for PCR measurements. */ - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRSIG)) - (void) pack_cpio_literal( - (uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRSIG].memory_offset, - sections[UNIFIED_SECTION_PCRSIG].size, - ".extra", - u"tpm2-pcr-signature.json", - /* dir_mode= */ 0555, - /* access_mode= */ 0444, - /* tpm_pcr= */ UINT32_MAX, - /* tpm_description= */ NULL, - initrds + INITRD_PCRSIG, - /* ret_measured= */ NULL); - - /* If the public key used for the PCR signatures was embedded in the PE image, then let's wrap it in - * a cpio and also pass it to the kernel, so that it can be read from - * /.extra/tpm2-pcr-public-key.pem. This section is already measure above, hence we won't measure the - * cpio. */ - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_PCRPKEY)) - (void) pack_cpio_literal( - (uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_PCRPKEY].memory_offset, - sections[UNIFIED_SECTION_PCRPKEY].size, - ".extra", - u"tpm2-pcr-public-key.pem", - /* dir_mode= */ 0555, - /* access_mode= */ 0444, - /* tpm_pcr= */ UINT32_MAX, - /* tpm_description= */ NULL, - initrds + INITRD_PCRPKEY, - /* ret_measured= */ NULL); + generate_embedded_initrds(loaded_image, sections, initrds); struct iovec kernel = IOVEC_MAKE( (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, From 8e67de84028d9357403580cce62aa57ef9702d23 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:34:09 +0200 Subject: [PATCH 17/36] stub: split out code that finds embedded initrds --- src/boot/efi/stub.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index eb3e905f3c..8947f3aabf 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -780,6 +780,26 @@ static void generate_embedded_initrds( /* ret_measured= */ NULL); } +static void lookup_embedded_initrds( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + PeSectionVector sections[_UNIFIED_SECTION_MAX], + struct iovec initrds[static _INITRD_MAX]) { + + assert(loaded_image); + assert(sections); + assert(initrds); + + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) + initrds[INITRD_BASE] = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset, + sections[UNIFIED_SECTION_INITRD].size); + + if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) + initrds[INITRD_UCODE] = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_UCODE].memory_offset, + sections[UNIFIED_SECTION_UCODE].size); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; @@ -930,15 +950,7 @@ static EFI_STATUS run(EFI_HANDLE image) { (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, sections[UNIFIED_SECTION_LINUX].size); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_INITRD)) - initrds[INITRD_BASE] = IOVEC_MAKE( - (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_INITRD].memory_offset, - sections[UNIFIED_SECTION_INITRD].size); - - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UCODE)) - initrds[INITRD_UCODE] = IOVEC_MAKE( - (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_UCODE].memory_offset, - sections[UNIFIED_SECTION_UCODE].size); + lookup_embedded_initrds(loaded_image, sections, initrds); _cleanup_pages_ Pages initrd_pages = {}; struct iovec final_initrd; From c802cb2f8048c3a1a10912eac10b68b062473f39 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:37:29 +0200 Subject: [PATCH 18/36] stub: move initialization of kernel iovec to the end, where it's used --- src/boot/efi/stub.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 8947f3aabf..4745556b02 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -945,11 +945,6 @@ static EFI_STATUS run(EFI_HANDLE image) { (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); generate_embedded_initrds(loaded_image, sections, initrds); - - struct iovec kernel = IOVEC_MAKE( - (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, - sections[UNIFIED_SECTION_LINUX].size); - lookup_embedded_initrds(loaded_image, sections, initrds); _cleanup_pages_ Pages initrd_pages = {}; @@ -968,6 +963,10 @@ static EFI_STATUS run(EFI_HANDLE image) { } else final_initrd = initrds[INITRD_BASE]; + struct iovec kernel = IOVEC_MAKE( + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, + sections[UNIFIED_SECTION_LINUX].size); + err = linux_exec(image, cmdline, kernel.iov_base, kernel.iov_len, final_initrd.iov_base, final_initrd.iov_len); From 8d5669a4679e716d6ecc34cad7c8a996aa8f27db Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:39:22 +0200 Subject: [PATCH 19/36] stub: split out code that sets EFI vars indicating measured PCRs --- src/boot/efi/stub.c | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 4745556b02..ae5d783e96 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -112,7 +112,7 @@ static EFI_STATUS combine_initrds( return EFI_SUCCESS; } -static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { +static void export_general_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { static const uint64_t stub_features = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */ @@ -161,7 +161,6 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); } - /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our * own data) */ (void) efivar_set(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); @@ -800,6 +799,25 @@ static void lookup_embedded_initrds( sections[UNIFIED_SECTION_UCODE].size); } +static void export_pcr_variables( + int sections_measured, + int parameters_measured, + int sysext_measured, + int confext_measured) { + + /* After we are done with measuring, set an EFI variable that tells userspace this was done + * successfully, and encode in it which PCR was used. */ + + if (sections_measured > 0) + (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0); + if (parameters_measured > 0) + (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0); + if (sysext_measured > 0) + (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0); + if (confext_measured > 0) + (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; @@ -870,11 +888,6 @@ static EFI_STATUS run(EFI_HANDLE image) { measure_sections(loaded_image, sections, §ions_measured); - /* After we are done, set an EFI variable that tells userspace this was done successfully, and encode - * in it which PCR was used. */ - if (sections_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelImage", TPM2_PCR_KERNEL_BOOT, 0); - /* Show splash screen as early as possible */ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_SPLASH)) graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size); @@ -903,7 +916,7 @@ static EFI_STATUS run(EFI_HANDLE image) { cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); - export_variables(loaded_image); + export_general_variables(loaded_image); generate_sidecar_initrds(loaded_image, initrds, ¶meters_measured, &sysext_measured, &confext_measured); @@ -937,12 +950,7 @@ static EFI_STATUS run(EFI_HANDLE image) { &dtb_measured); combine_measured_flag(¶meters_measured, dtb_measured); - if (parameters_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrKernelParameters", TPM2_PCR_KERNEL_CONFIG, 0); - if (sysext_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDSysExts", TPM2_PCR_SYSEXTS, 0); - if (confext_measured > 0) - (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); + export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); generate_embedded_initrds(loaded_image, sections, initrds); lookup_embedded_initrds(loaded_image, sections, initrds); From dcac1e4a9ba231d8e88d36dbecf3d8b6c9b07cb2 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 10:48:49 +0200 Subject: [PATCH 20/36] stub: split out call that loads embedded device tree --- src/boot/efi/stub.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index ae5d783e96..2bea86ff99 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -818,13 +818,34 @@ static void export_pcr_variables( (void) efivar_set_uint_string(MAKE_GUID_PTR(LOADER), u"StubPcrInitRDConfExts", TPM2_PCR_KERNEL_CONFIG, 0); } +static void install_embedded_devicetree( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX], + struct devicetree_state *dt_state) { + + EFI_STATUS err; + + assert(loaded_image); + assert(sections); + assert(dt_state); + + if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) + return; + + err = devicetree_install_from_memory( + dt_state, + (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, + sections[UNIFIED_SECTION_DTB].size); + if (err != EFI_SUCCESS) + log_error_status(err, "Error loading embedded devicetree, igoring: %m"); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL; _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL; - size_t dt_size = 0, n_dts_addons_global = 0, n_dts_addons_uki = 0; - EFI_PHYSICAL_ADDRESS dt_base = 0; + size_t n_dts_addons_global = 0, n_dts_addons_uki = 0; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; @@ -920,18 +941,8 @@ static EFI_STATUS run(EFI_HANDLE image) { generate_sidecar_initrds(loaded_image, initrds, ¶meters_measured, &sysext_measured, &confext_measured); - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { - dt_size = sections[UNIFIED_SECTION_DTB].size; - dt_base = POINTER_TO_PHYSICAL_ADDRESS(loaded_image->ImageBase) + sections[UNIFIED_SECTION_DTB].memory_offset; - } - /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */ - if (dt_size > 0) { - err = devicetree_install_from_memory( - &dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size); - if (err != EFI_SUCCESS) - log_error_status(err, "Error loading embedded devicetree: %m"); - } + install_embedded_devicetree(loaded_image, sections, &dt_state); int dtb_measured; dtb_install_addons(&dt_state, From 9e2ed6c78fd22f683e3360f7b58c424f93d9f6e1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 11:18:51 +0200 Subject: [PATCH 21/36] stub: add DevicetreeAddon structure Instead of keeping three parallel arrays of dt base, dt size and dt filename, just introduce a proper structure and use an array of that, greatly simplifying DT handling. --- src/boot/efi/stub.c | 194 ++++++++++++++++++-------------------------- 1 file changed, 80 insertions(+), 114 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 2bea86ff99..363184ef10 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -312,100 +312,90 @@ static void cmdline_append_and_measure_addons( *cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", merged); } -static void dtb_install_addons( +typedef struct DevicetreeAddon { + char16_t *filename; + struct iovec blob; +} DevicetreeAddon; + +static void devicetree_addon_done(DevicetreeAddon *a) { + assert(a); + + a->filename = mfree(a->filename); + iovec_done(&a->blob); +} + +static void devicetree_addon_free_many(DevicetreeAddon *a, size_t n) { + assert(a || n == 0); + + FOREACH_ARRAY(i, a, n) + devicetree_addon_done(i); + + free(a); +} + +static void install_addon_devicetrees( struct devicetree_state *dt_state, - void **dt_bases, - size_t *dt_sizes, - char16_t **dt_filenames, - size_t n_dts, + DevicetreeAddon *addons, + size_t n_addons, int *ret_parameters_measured) { int parameters_measured = -1; EFI_STATUS err; assert(dt_state); - assert(n_dts == 0 || (dt_bases && dt_sizes && dt_filenames)); + assert(addons || n_addons == 0); assert(ret_parameters_measured); - for (size_t i = 0; i < n_dts; ++i) { - err = devicetree_install_from_memory(dt_state, dt_bases[i], dt_sizes[i]); - if (err != EFI_SUCCESS) + FOREACH_ARRAY(a, addons, n_addons) { + err = devicetree_install_from_memory(dt_state, a->blob.iov_base, a->blob.iov_len); + if (err != EFI_SUCCESS) { log_error_status(err, "Error loading addon devicetree, ignoring: %m"); - else { - bool m = false; - - err = tpm_log_tagged_event( - TPM2_PCR_KERNEL_CONFIG, - POINTER_TO_PHYSICAL_ADDRESS(dt_bases[i]), - dt_sizes[i], - DEVICETREE_ADDON_EVENT_TAG_ID, - dt_filenames[i], - &m); - if (err != EFI_SUCCESS) - return (void) log_error_status( - err, - "Unable to add measurement of DTB addon #%zu to PCR %i: %m", - i, - TPM2_PCR_KERNEL_CONFIG); - - combine_measured_flag(¶meters_measured, m); + continue; } + + bool m = false; + err = tpm_log_tagged_event( + TPM2_PCR_KERNEL_CONFIG, + POINTER_TO_PHYSICAL_ADDRESS(a->blob.iov_base), + a->blob.iov_len, + DEVICETREE_ADDON_EVENT_TAG_ID, + a->filename, + &m); + if (err != EFI_SUCCESS) + return (void) log_error_status( + err, + "Unable to extend PCR %i with DTB addon '%ls': %m", + TPM2_PCR_KERNEL_CONFIG, + a->filename); + + combine_measured_flag(¶meters_measured, m); } *ret_parameters_measured = parameters_measured; } -static void dt_bases_free(void **dt_bases, size_t n_dt) { - assert(dt_bases || n_dt == 0); - - for (size_t i = 0; i < n_dt; ++i) - free(dt_bases[i]); - - free(dt_bases); -} - -static void dt_filenames_free(char16_t **dt_filenames, size_t n_dt) { - assert(dt_filenames || n_dt == 0); - - for (size_t i = 0; i < n_dt; ++i) - free(dt_filenames[i]); - - free(dt_filenames); -} - static EFI_STATUS load_addons( EFI_HANDLE stub_image, EFI_LOADED_IMAGE_PROTOCOL *loaded_image, const char16_t *prefix, const char *uname, char16_t **ret_cmdline, - void ***ret_dt_bases, - size_t **ret_dt_sizes, - char16_t ***ret_dt_filenames, - size_t *ret_n_dt) { + DevicetreeAddon **ret_devicetree_addons, + size_t *ret_n_devicetree_addons) { - _cleanup_free_ size_t *dt_sizes = NULL; _cleanup_(strv_freep) char16_t **items = NULL; _cleanup_(file_closep) EFI_FILE *root = NULL; _cleanup_free_ char16_t *cmdline = NULL; - size_t n_items = 0, n_allocated = 0, n_dt = 0; - char16_t **dt_filenames = NULL; - void **dt_bases = NULL; + size_t n_items = 0, n_allocated = 0; EFI_STATUS err; assert(stub_image); assert(loaded_image); assert(prefix); - assert(!!ret_dt_bases == !!ret_dt_sizes); - assert(!!ret_dt_bases == !!ret_n_dt); - assert(!!ret_dt_filenames == !!ret_n_dt); if (!loaded_image->DeviceHandle) return EFI_SUCCESS; - CLEANUP_ARRAY(dt_bases, n_dt, dt_bases_free); - CLEANUP_ARRAY(dt_filenames, n_dt, dt_filenames_free); - err = open_volume(loaded_image->DeviceHandle, &root); if (err == EFI_UNSUPPORTED) /* Error will be unsupported if the bootloader doesn't implement the file system protocol on @@ -425,6 +415,10 @@ static EFI_STATUS load_addons( * are not dependent on read order) */ sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16); + DevicetreeAddon *dt_addons = NULL; + size_t n_dt_addons = 0; + CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); + for (size_t i = 0; i < n_items; i++) { PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL; @@ -489,36 +483,27 @@ static EFI_STATUS load_addons( cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); } - if (ret_dt_bases && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { - dt_sizes = xrealloc(dt_sizes, - n_dt * sizeof(size_t), - (n_dt + 1) * sizeof(size_t)); - dt_sizes[n_dt] = sections[UNIFIED_SECTION_DTB].size; + if (ret_devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { + dt_addons = xrealloc(dt_addons, + n_dt_addons * sizeof(size_t), + (n_dt_addons + 1) * sizeof(size_t)); - dt_bases = xrealloc(dt_bases, - n_dt * sizeof(void *), - (n_dt + 1) * sizeof(void *)); - dt_bases[n_dt] = xmemdup((uint8_t*)loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, - dt_sizes[n_dt]); - - dt_filenames = xrealloc(dt_filenames, - n_dt * sizeof(char16_t *), - (n_dt + 1) * sizeof(char16_t *)); - dt_filenames[n_dt] = xstrdup16(items[i]); - - ++n_dt; + dt_addons[n_dt_addons++] = (DevicetreeAddon) { + .blob = { + .iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, sections[UNIFIED_SECTION_DTB].size), + .iov_len = sections[UNIFIED_SECTION_DTB].size, + }, + .filename = xstrdup16(items[i]), + }; } } - if (ret_cmdline && !isempty(cmdline)) + if (ret_cmdline) *ret_cmdline = TAKE_PTR(cmdline); - - if (ret_n_dt && n_dt > 0) { - *ret_dt_filenames = TAKE_PTR(dt_filenames); - *ret_dt_bases = TAKE_PTR(dt_bases); - *ret_dt_sizes = TAKE_PTR(dt_sizes); - *ret_n_dt = n_dt; - } + if (ret_devicetree_addons) + *ret_devicetree_addons = TAKE_PTR(dt_addons); + if (ret_n_devicetree_addons) + *ret_n_devicetree_addons = n_dt_addons; return EFI_SUCCESS; } @@ -842,10 +827,8 @@ static void install_embedded_devicetree( static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; - void **dt_bases_addons_global = NULL, **dt_bases_addons_uki = NULL; - char16_t **dt_filenames_addons_global = NULL, **dt_filenames_addons_uki = NULL; - _cleanup_free_ size_t *dt_sizes_addons_global = NULL, *dt_sizes_addons_uki = NULL; - size_t n_dts_addons_global = 0, n_dts_addons_uki = 0; + DevicetreeAddon *dt_addons_global = NULL, *dt_addons_local = NULL; + size_t n_dt_addons_global = 0, n_dt_addons_local = 0; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; @@ -868,13 +851,11 @@ static EFI_STATUS run(EFI_HANDLE image) { return log_error_status(err, "Unable to locate embedded .linux section: %m"); } - CLEANUP_ARRAY(dt_bases_addons_global, n_dts_addons_global, dt_bases_free); - CLEANUP_ARRAY(dt_bases_addons_uki, n_dts_addons_uki, dt_bases_free); - CLEANUP_ARRAY(dt_filenames_addons_global, n_dts_addons_global, dt_filenames_free); - CLEANUP_ARRAY(dt_filenames_addons_uki, n_dts_addons_uki, dt_filenames_free); - lookup_uname(loaded_image, sections, &uname); + CLEANUP_ARRAY(dt_addons_global, n_dt_addons_local, devicetree_addon_free_many); + CLEANUP_ARRAY(dt_addons_local, n_dt_addons_local, devicetree_addon_free_many); + /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ err = load_addons( @@ -883,10 +864,8 @@ static EFI_STATUS run(EFI_HANDLE image) { u"\\loader\\addons", uname, &cmdline_addons_global, - &dt_bases_addons_global, - &dt_sizes_addons_global, - &dt_filenames_addons_global, - &n_dts_addons_global); + &dt_addons_global, + &n_dt_addons_global); if (err != EFI_SUCCESS) log_error_status(err, "Error loading global addons, ignoring: %m"); @@ -899,10 +878,8 @@ static EFI_STATUS run(EFI_HANDLE image) { dropin_dir, uname, &cmdline_addons_uki, - &dt_bases_addons_uki, - &dt_sizes_addons_uki, - &dt_filenames_addons_uki, - &n_dts_addons_uki); + &dt_addons_local, + &n_dt_addons_local); if (err != EFI_SUCCESS) log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); } @@ -945,20 +922,9 @@ static EFI_STATUS run(EFI_HANDLE image) { install_embedded_devicetree(loaded_image, sections, &dt_state); int dtb_measured; - dtb_install_addons(&dt_state, - dt_bases_addons_global, - dt_sizes_addons_global, - dt_filenames_addons_global, - n_dts_addons_global, - &dtb_measured); + install_addon_devicetrees(&dt_state, dt_addons_global, n_dt_addons_global, &dtb_measured); combine_measured_flag(¶meters_measured, dtb_measured); - - dtb_install_addons(&dt_state, - dt_bases_addons_uki, - dt_sizes_addons_uki, - dt_filenames_addons_uki, - n_dts_addons_uki, - &dtb_measured); + install_addon_devicetrees(&dt_state, dt_addons_local, n_dt_addons_local, &dtb_measured); combine_measured_flag(¶meters_measured, dtb_measured); export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); From acf3e58eb3be9f73ea80667fbbe242814b94e0a4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 11:21:26 +0200 Subject: [PATCH 22/36] stub: don't make up errors --- src/boot/efi/stub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 363184ef10..499a473d6b 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -462,7 +462,7 @@ static EFI_STATUS load_addons( /* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */ if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { - log_error_status(EFI_INVALID_PARAMETER, "%ls is a UKI, not an addon, ignoring: %m", items[i]); + log_error("%ls is a UKI, not an addon, ignoring.", items[i]); continue; } From 16b550d01a252c98df267166ac569ef773cc7a34 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 11:36:03 +0200 Subject: [PATCH 23/36] stub: merge separate lists for addon cmdlines/devicetrees into one Instead of keeping the lists for the global and per-UKI addons separate throughout, just merge them. We apply them in the same order after all. --- src/boot/efi/stub.c | 92 ++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 56 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 499a473d6b..abeb6b6c48 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -281,35 +281,30 @@ static EFI_STATUS load_addons_from_dir( } static void cmdline_append_and_measure_addons( - char16_t *cmdline_global, - char16_t *cmdline_uki, + char16_t *cmdline_addon, char16_t **cmdline_append, bool *ret_parameters_measured) { - _cleanup_free_ char16_t *tmp = NULL, *merged = NULL; - bool m = false; - assert(cmdline_append); assert(ret_parameters_measured); - if (isempty(cmdline_global) && isempty(cmdline_uki)) + if (isempty(cmdline_addon)) return; - merged = xasprintf("%ls%ls%ls", - strempty(cmdline_global), - isempty(cmdline_global) || isempty(cmdline_uki) ? u"" : u" ", - strempty(cmdline_uki)); - - mangle_stub_cmdline(merged); - - if (isempty(merged)) + _cleanup_free_ char16_t *copy = xstrdup16(cmdline_addon); + mangle_stub_cmdline(copy); + if (isempty(copy)) return; - (void) tpm_log_load_options(merged, &m); + bool m = false; + (void) tpm_log_load_options(copy, &m); *ret_parameters_measured = m; - tmp = TAKE_PTR(*cmdline_append); - *cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", merged); + _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append); + if (isempty(tmp)) + *cmdline_append = TAKE_PTR(copy); + else + *cmdline_append = xasprintf("%ls %ls", tmp, copy); } typedef struct DevicetreeAddon { @@ -379,13 +374,12 @@ static EFI_STATUS load_addons( EFI_LOADED_IMAGE_PROTOCOL *loaded_image, const char16_t *prefix, const char *uname, - char16_t **ret_cmdline, - DevicetreeAddon **ret_devicetree_addons, - size_t *ret_n_devicetree_addons) { + char16_t **cmdline, /* Both input+output, extended with new addons we find */ + DevicetreeAddon **devicetree_addons, /* Ditto */ + size_t *n_devicetree_addons) { _cleanup_(strv_freep) char16_t **items = NULL; _cleanup_(file_closep) EFI_FILE *root = NULL; - _cleanup_free_ char16_t *cmdline = NULL; size_t n_items = 0, n_allocated = 0; EFI_STATUS err; @@ -415,10 +409,6 @@ static EFI_STATUS load_addons( * are not dependent on read order) */ sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16); - DevicetreeAddon *dt_addons = NULL; - size_t n_dt_addons = 0; - CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); - for (size_t i = 0; i < n_items; i++) { PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL; @@ -470,25 +460,25 @@ static EFI_STATUS load_addons( * enforcing compatibility with a specific UKI only */ if (uname && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME) && !strneq8(uname, - (char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, + (const char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, sections[UNIFIED_SECTION_UNAME].size)) { log_error(".uname mismatch between %ls and UKI, ignoring", items[i]); continue; } - if (ret_cmdline && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { - _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), + if (cmdline && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { + _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline), *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, sections[UNIFIED_SECTION_CMDLINE].size); - cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); + *cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); } - if (ret_devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { - dt_addons = xrealloc(dt_addons, - n_dt_addons * sizeof(size_t), - (n_dt_addons + 1) * sizeof(size_t)); + if (devicetree_addons && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_DTB)) { + *devicetree_addons = xrealloc(*devicetree_addons, + *n_devicetree_addons * sizeof(size_t), + (*n_devicetree_addons + 1) * sizeof(size_t)); - dt_addons[n_dt_addons++] = (DevicetreeAddon) { + *devicetree_addons[(*n_devicetree_addons)++] = (DevicetreeAddon) { .blob = { .iov_base = xmemdup((const uint8_t*) loaded_addon->ImageBase + sections[UNIFIED_SECTION_DTB].memory_offset, sections[UNIFIED_SECTION_DTB].size), .iov_len = sections[UNIFIED_SECTION_DTB].size, @@ -498,13 +488,6 @@ static EFI_STATUS load_addons( } } - if (ret_cmdline) - *ret_cmdline = TAKE_PTR(cmdline); - if (ret_devicetree_addons) - *ret_devicetree_addons = TAKE_PTR(dt_addons); - if (ret_n_devicetree_addons) - *ret_n_devicetree_addons = n_dt_addons; - return EFI_SUCCESS; } @@ -827,12 +810,12 @@ static void install_embedded_devicetree( static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; - DevicetreeAddon *dt_addons_global = NULL, *dt_addons_local = NULL; - size_t n_dt_addons_global = 0, n_dt_addons_local = 0; + DevicetreeAddon *dt_addons = NULL; + size_t n_dt_addons = 0; _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; EFI_LOADED_IMAGE_PROTOCOL *loaded_image; PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; - _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons_global = NULL, *cmdline_addons_uki = NULL; + _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons = NULL; int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; _cleanup_free_ char *uname = NULL; bool m; @@ -853,8 +836,7 @@ static EFI_STATUS run(EFI_HANDLE image) { lookup_uname(loaded_image, sections, &uname); - CLEANUP_ARRAY(dt_addons_global, n_dt_addons_local, devicetree_addon_free_many); - CLEANUP_ARRAY(dt_addons_local, n_dt_addons_local, devicetree_addon_free_many); + CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ @@ -863,9 +845,9 @@ static EFI_STATUS run(EFI_HANDLE image) { loaded_image, u"\\loader\\addons", uname, - &cmdline_addons_global, - &dt_addons_global, - &n_dt_addons_global); + &cmdline_addons, + &dt_addons, + &n_dt_addons); if (err != EFI_SUCCESS) log_error_status(err, "Error loading global addons, ignoring: %m"); @@ -877,9 +859,9 @@ static EFI_STATUS run(EFI_HANDLE image) { loaded_image, dropin_dir, uname, - &cmdline_addons_uki, - &dt_addons_local, - &n_dt_addons_local); + &cmdline_addons, + &dt_addons, + &n_dt_addons); if (err != EFI_SUCCESS) log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); } @@ -909,7 +891,7 @@ static EFI_STATUS run(EFI_HANDLE image) { * measure the additions together, after the embedded options, but before the smbios ones, * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are * loaded first, and the image-specific ones later, for the same reason. */ - cmdline_append_and_measure_addons(cmdline_addons_global, cmdline_addons_uki, &cmdline, &m); + cmdline_append_and_measure_addons(cmdline_addons, &cmdline, &m); combine_measured_flag(¶meters_measured, m); cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); @@ -922,9 +904,7 @@ static EFI_STATUS run(EFI_HANDLE image) { install_embedded_devicetree(loaded_image, sections, &dt_state); int dtb_measured; - install_addon_devicetrees(&dt_state, dt_addons_global, n_dt_addons_global, &dtb_measured); - combine_measured_flag(¶meters_measured, dtb_measured); - install_addon_devicetrees(&dt_state, dt_addons_local, n_dt_addons_local, &dtb_measured); + install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, &dtb_measured); combine_measured_flag(¶meters_measured, dtb_measured); export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); From 1489e66f7cc1afeaee8e27b9d94d09621dc4856b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 11:58:17 +0200 Subject: [PATCH 24/36] stub: split out code that loads all addons from disk into function of its own --- src/boot/efi/stub.c | 72 ++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index abeb6b6c48..b8daf1d88f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -808,6 +808,49 @@ static void install_embedded_devicetree( log_error_status(err, "Error loading embedded devicetree, igoring: %m"); } +static void load_all_addons( + EFI_HANDLE image, + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const char *uname, + char16_t **cmdline_addons, + DevicetreeAddon **dt_addons, + size_t *n_dt_addons) { + + EFI_STATUS err; + + assert(loaded_image); + assert(cmdline_addons); + assert(dt_addons); + assert(n_dt_addons); + + err = load_addons( + image, + loaded_image, + u"\\loader\\addons", + uname, + cmdline_addons, + dt_addons, + n_dt_addons); + if (err != EFI_SUCCESS) + log_error_status(err, "Error loading global addons, ignoring: %m"); + + /* Some bootloaders always pass NULL in FilePath, so we need to check for it here. */ + _cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath); + if (!dropin_dir) + return; + + err = load_addons( + image, + loaded_image, + dropin_dir, + uname, + cmdline_addons, + dt_addons, + n_dt_addons); + if (err != EFI_SUCCESS) + log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; DevicetreeAddon *dt_addons = NULL; @@ -836,35 +879,10 @@ static EFI_STATUS run(EFI_HANDLE image) { lookup_uname(loaded_image, sections, &uname); - CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); - /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ - err = load_addons( - image, - loaded_image, - u"\\loader\\addons", - uname, - &cmdline_addons, - &dt_addons, - &n_dt_addons); - if (err != EFI_SUCCESS) - log_error_status(err, "Error loading global addons, ignoring: %m"); - - /* Some bootloaders always pass NULL in FilePath, so we need to check for it here. */ - _cleanup_free_ char16_t *dropin_dir = get_extra_dir(loaded_image->FilePath); - if (dropin_dir) { - err = load_addons( - image, - loaded_image, - dropin_dir, - uname, - &cmdline_addons, - &dt_addons, - &n_dt_addons); - if (err != EFI_SUCCESS) - log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); - } + CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); + load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons); measure_sections(loaded_image, sections, §ions_measured); From 248b3257a17e50a1f7bf214d9b1dfe417e3958a0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:00:46 +0200 Subject: [PATCH 25/36] stub: split out code that displays boot splash --- src/boot/efi/stub.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index b8daf1d88f..5c07b66ea8 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -851,6 +851,19 @@ static void load_all_addons( log_error_status(err, "Error loading UKI-specific addons, ignoring: %m"); } +static void display_splash( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX]) { + + assert(loaded_image); + assert(sections); + + if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_SPLASH)) + return; + + graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size); +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; DevicetreeAddon *dt_addons = NULL; @@ -887,8 +900,7 @@ static EFI_STATUS run(EFI_HANDLE image) { measure_sections(loaded_image, sections, §ions_measured); /* Show splash screen as early as possible */ - if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_SPLASH)) - graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size); + display_splash(loaded_image, sections); if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), &cmdline)) { /* Let's measure the passed kernel command line into the TPM. Note that this possibly From 9f3bc7b505197c72dedc7fd169eacfbbd3d7bc2d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:02:06 +0200 Subject: [PATCH 26/36] stub: slightly reorder things Let's do the section measurement first, before we use any data of it. Let's bring up the boot splash next, so that it covers anything else we might do. --- src/boot/efi/stub.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 5c07b66ea8..a0237bf112 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -881,8 +881,6 @@ static EFI_STATUS run(EFI_HANDLE image) { if (err != EFI_SUCCESS) return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m"); - refresh_random_seed(loaded_image); - err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, sections); if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { if (err == EFI_SUCCESS) @@ -890,6 +888,13 @@ static EFI_STATUS run(EFI_HANDLE image) { return log_error_status(err, "Unable to locate embedded .linux section: %m"); } + measure_sections(loaded_image, sections, §ions_measured); + + /* Show splash screen as early as possible, but after measuring it */ + display_splash(loaded_image, sections); + + refresh_random_seed(loaded_image); + lookup_uname(loaded_image, sections, &uname); /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) @@ -897,11 +902,6 @@ static EFI_STATUS run(EFI_HANDLE image) { CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons); - measure_sections(loaded_image, sections, §ions_measured); - - /* Show splash screen as early as possible */ - display_splash(loaded_image, sections); - if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), &cmdline)) { /* Let's measure the passed kernel command line into the TPM. Note that this possibly * duplicates what we already did in the boot menu, if that was already used. However, since From 9597320f5c39777c85d3b1fafd8660d58895a2a1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:05:04 +0200 Subject: [PATCH 27/36] stub: normalize error handling when looking for PE sections --- src/boot/efi/stub.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index a0237bf112..0fc9fd4eff 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -882,11 +882,10 @@ static EFI_STATUS run(EFI_HANDLE image) { return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m"); err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, sections); - if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) { - if (err == EFI_SUCCESS) - err = EFI_NOT_FOUND; - return log_error_status(err, "Unable to locate embedded .linux section: %m"); - } + if (err != EFI_SUCCESS) + return log_error_status(err, "Unable to locate embedded PE sections: %m"); + if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_LINUX)) + return log_error_status(EFI_NOT_FOUND, "Image lacks .linux section."); measure_sections(loaded_image, sections, §ions_measured); From fe0f6c099c8baaa2263b56e997dc6def51aad382 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:11:46 +0200 Subject: [PATCH 28/36] stub: split out function that determines main cmdline --- src/boot/efi/stub.c | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 0fc9fd4eff..44dab8e878 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -864,6 +864,32 @@ static void display_splash( graphics_splash((const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_SPLASH].memory_offset, sections[UNIFIED_SECTION_SPLASH].size); } +static void determine_cmdline( + EFI_HANDLE image, + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector sections[static _UNIFIED_SECTION_MAX], + char16_t **ret_cmdline, + int *parameters_measured) { + + assert(loaded_image); + assert(sections); + + if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), ret_cmdline)) { + /* Let's measure the passed kernel command line into the TPM. Note that this possibly + * duplicates what we already did in the boot menu, if that was already used. However, since + * we want the boot menu to support an EFI binary, and want to this stub to be usable from + * any boot menu, let's measure things anyway. */ + bool m = false; + (void) tpm_log_load_options(*ret_cmdline, &m); + combine_measured_flag(parameters_measured, m); + } else if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { + *ret_cmdline = xstrn8_to_16( + (char *) loaded_image->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, + sections[UNIFIED_SECTION_CMDLINE].size); + mangle_stub_cmdline(*ret_cmdline); + } +} + static EFI_STATUS run(EFI_HANDLE image) { _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; DevicetreeAddon *dt_addons = NULL; @@ -896,26 +922,13 @@ static EFI_STATUS run(EFI_HANDLE image) { lookup_uname(loaded_image, sections, &uname); + determine_cmdline(image, loaded_image, sections, &cmdline, ¶meters_measured); + /* Now that we have the UKI sections loaded, also load global first and then local (per-UKI) * addons. The data is loaded at once, and then used later. */ CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons); - if (use_load_options(image, loaded_image, /* have_cmdline= */ PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE), &cmdline)) { - /* Let's measure the passed kernel command line into the TPM. Note that this possibly - * duplicates what we already did in the boot menu, if that was already used. However, since - * we want the boot menu to support an EFI binary, and want to this stub to be usable from - * any boot menu, let's measure things anyway. */ - m = false; - (void) tpm_log_load_options(cmdline, &m); - combine_measured_flag(¶meters_measured, m); - } else if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { - cmdline = xstrn8_to_16( - (char *) loaded_image->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, - sections[UNIFIED_SECTION_CMDLINE].size); - mangle_stub_cmdline(cmdline); - } - /* If we have any extra command line to add via PE addons, load them now and append, and * measure the additions together, after the embedded options, but before the smbios ones, * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are From e864cd91d53e14a96c3679f7fe9a70eeb4cba9a3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:13:45 +0200 Subject: [PATCH 29/36] stub: uniformly process "measured" flags Always pass the pointer through so that the functions combine the flags directly, instead of doing that in the caller. --- src/boot/efi/stub.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 44dab8e878..f0453b3bff 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -283,10 +283,10 @@ static EFI_STATUS load_addons_from_dir( static void cmdline_append_and_measure_addons( char16_t *cmdline_addon, char16_t **cmdline_append, - bool *ret_parameters_measured) { + int *parameters_measured) { assert(cmdline_append); - assert(ret_parameters_measured); + assert(parameters_measured); if (isempty(cmdline_addon)) return; @@ -298,7 +298,7 @@ static void cmdline_append_and_measure_addons( bool m = false; (void) tpm_log_load_options(copy, &m); - *ret_parameters_measured = m; + combine_measured_flag(parameters_measured, m); _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append); if (isempty(tmp)) @@ -332,14 +332,13 @@ static void install_addon_devicetrees( struct devicetree_state *dt_state, DevicetreeAddon *addons, size_t n_addons, - int *ret_parameters_measured) { + int *parameters_measured) { - int parameters_measured = -1; EFI_STATUS err; assert(dt_state); assert(addons || n_addons == 0); - assert(ret_parameters_measured); + assert(parameters_measured); FOREACH_ARRAY(a, addons, n_addons) { err = devicetree_install_from_memory(dt_state, a->blob.iov_base, a->blob.iov_len); @@ -363,10 +362,8 @@ static void install_addon_devicetrees( TPM2_PCR_KERNEL_CONFIG, a->filename); - combine_measured_flag(¶meters_measured, m); + combine_measured_flag(parameters_measured, m); } - - *ret_parameters_measured = parameters_measured; } static EFI_STATUS load_addons( @@ -900,7 +897,6 @@ static EFI_STATUS run(EFI_HANDLE image) { _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons = NULL; int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; _cleanup_free_ char *uname = NULL; - bool m; EFI_STATUS err; err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image); @@ -929,13 +925,11 @@ static EFI_STATUS run(EFI_HANDLE image) { CLEANUP_ARRAY(dt_addons, n_dt_addons, devicetree_addon_free_many); load_all_addons(image, loaded_image, uname, &cmdline_addons, &dt_addons, &n_dt_addons); - /* If we have any extra command line to add via PE addons, load them now and append, and - * measure the additions together, after the embedded options, but before the smbios ones, - * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are - * loaded first, and the image-specific ones later, for the same reason. */ - cmdline_append_and_measure_addons(cmdline_addons, &cmdline, &m); - combine_measured_flag(¶meters_measured, m); - + /* If we have any extra command line to add via PE addons, load them now and append, and measure the + * additions together, after the embedded options, but before the smbios ones, so that the order is + * reversed from "most hardcoded" to "most dynamic". The global addons are loaded first, and the + * image-specific ones later, for the same reason. */ + cmdline_append_and_measure_addons(cmdline_addons, &cmdline, ¶meters_measured); cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); export_general_variables(loaded_image); @@ -945,9 +939,7 @@ static EFI_STATUS run(EFI_HANDLE image) { /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */ install_embedded_devicetree(loaded_image, sections, &dt_state); - int dtb_measured; - install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, &dtb_measured); - combine_measured_flag(¶meters_measured, dtb_measured); + install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, ¶meters_measured); export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); From 4087aa38c5069f6a51e86684b96394cad6925715 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:22:33 +0200 Subject: [PATCH 30/36] stub: reorder things a bit, so that initrds are generated/looked up together --- src/boot/efi/stub.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index f0453b3bff..08dc705dcf 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -934,18 +934,19 @@ static EFI_STATUS run(EFI_HANDLE image) { export_general_variables(loaded_image); - generate_sidecar_initrds(loaded_image, initrds, ¶meters_measured, &sysext_measured, &confext_measured); - /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */ install_embedded_devicetree(loaded_image, sections, &dt_state); - install_addon_devicetrees(&dt_state, dt_addons, n_dt_addons, ¶meters_measured); - export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); - + /* Generate & find all initrds */ + generate_sidecar_initrds(loaded_image, initrds, ¶meters_measured, &sysext_measured, &confext_measured); generate_embedded_initrds(loaded_image, sections, initrds); lookup_embedded_initrds(loaded_image, sections, initrds); + /* Export variables indicating what we measured */ + export_pcr_variables(sections_measured, parameters_measured, sysext_measured, confext_measured); + + /* Combine the initrds into one */ _cleanup_pages_ Pages initrd_pages = {}; struct iovec final_initrd; if (initrds_need_combine(initrds)) { From c8af9aca9e506aac78ab3130dc79d500da860723 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:23:57 +0200 Subject: [PATCH 31/36] stub: reorder variables --- src/boot/efi/stub.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 08dc705dcf..2317b505d9 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -888,15 +888,15 @@ static void determine_cmdline( } static EFI_STATUS run(EFI_HANDLE image) { + int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; + _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; + _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons = NULL; _cleanup_(initrds_free) struct iovec initrds[_INITRD_MAX] = {}; + PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; + EFI_LOADED_IMAGE_PROTOCOL *loaded_image; + _cleanup_free_ char *uname = NULL; DevicetreeAddon *dt_addons = NULL; size_t n_dt_addons = 0; - _cleanup_(devicetree_cleanup) struct devicetree_state dt_state = {}; - EFI_LOADED_IMAGE_PROTOCOL *loaded_image; - PeSectionVector sections[ELEMENTSOF(unified_sections)] = {}; - _cleanup_free_ char16_t *cmdline = NULL, *cmdline_addons = NULL; - int sections_measured = -1, parameters_measured = -1, sysext_measured = -1, confext_measured = -1; - _cleanup_free_ char *uname = NULL; EFI_STATUS err; err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image); From c36c088a93577862e85815eecec2a51a0127ad43 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:35:12 +0200 Subject: [PATCH 32/36] stub: add helper that turns PE section into char16_t* string --- src/boot/efi/stub.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 2317b505d9..03b6fc8153 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -44,6 +44,19 @@ DECLARE_NOALLOC_SECTION(".sdmagic", "#### LoaderInfo: systemd-stub " GIT_VERSION DECLARE_SBAT(SBAT_STUB_SECTION_TEXT); +static char16_t* pe_section_to_str16( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector *section) { + + assert(loaded_image); + assert(section); + + if (!PE_SECTION_VECTOR_IS_SET(section)) + return NULL; + + return xstrn8_to_16((const char *) loaded_image->ImageBase + section->memory_offset, section->size); +} + static void combine_measured_flag(int *value, int measured) { assert(value); @@ -465,8 +478,8 @@ static EFI_STATUS load_addons( if (cmdline && PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline), - *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, - sections[UNIFIED_SECTION_CMDLINE].size); + *extra16 = pe_section_to_str16(loaded_addon, sections + UNIFIED_SECTION_CMDLINE); + *cmdline = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16); } @@ -879,11 +892,10 @@ static void determine_cmdline( bool m = false; (void) tpm_log_load_options(*ret_cmdline, &m); combine_measured_flag(parameters_measured, m); - } else if (PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_CMDLINE)) { - *ret_cmdline = xstrn8_to_16( - (char *) loaded_image->ImageBase + sections[UNIFIED_SECTION_CMDLINE].memory_offset, - sections[UNIFIED_SECTION_CMDLINE].size); - mangle_stub_cmdline(*ret_cmdline); + } else { + *ret_cmdline = pe_section_to_str16(loaded_image, sections + UNIFIED_SECTION_CMDLINE); + if (*ret_cmdline) + mangle_stub_cmdline(*ret_cmdline); } } From a729187a19548941cabe144cd5287b3d71d38965 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 12:35:46 +0200 Subject: [PATCH 33/36] stub: turn lookup_name() into shorter and more generic function that turns sectin into char* string --- src/boot/efi/stub.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 03b6fc8153..98d31af321 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -57,6 +57,19 @@ static char16_t* pe_section_to_str16( return xstrn8_to_16((const char *) loaded_image->ImageBase + section->memory_offset, section->size); } +static char *pe_section_to_str8( + EFI_LOADED_IMAGE_PROTOCOL *loaded_image, + const PeSectionVector *section) { + + assert(loaded_image); + assert(section); + + if (!PE_SECTION_VECTOR_IS_SET(section)) + return NULL; + + return xstrndup8((const char *)loaded_image->ImageBase + section->memory_offset, section->size); +} + static void combine_measured_flag(int *value, int measured) { assert(value); @@ -601,24 +614,6 @@ static void cmdline_append_and_measure_smbios(char16_t **cmdline, int *parameter *cmdline = xasprintf("%ls %ls", tmp, extra16); } -static void lookup_uname( - EFI_LOADED_IMAGE_PROTOCOL *loaded_image, - const PeSectionVector sections[static _UNIFIED_SECTION_MAX], - char **ret_uname) { - - assert(loaded_image); - assert(sections); - assert(ret_uname); - - if (!PE_SECTION_VECTOR_IS_SET(sections + UNIFIED_SECTION_UNAME)) { - *ret_uname = NULL; - return; - } - - *ret_uname = xstrndup8((char *)loaded_image->ImageBase + sections[UNIFIED_SECTION_UNAME].memory_offset, - sections[UNIFIED_SECTION_UNAME].size); -} - static void initrds_free(struct iovec (*initrds)[_INITRD_MAX]) { assert(initrds); @@ -928,7 +923,7 @@ static EFI_STATUS run(EFI_HANDLE image) { refresh_random_seed(loaded_image); - lookup_uname(loaded_image, sections, &uname); + uname = pe_section_to_str8(loaded_image, sections + UNIFIED_SECTION_UNAME); determine_cmdline(image, loaded_image, sections, &cmdline, ¶meters_measured); From 4a3659c5f400cbcddbe251ec980798d43db66781 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 13:16:18 +0200 Subject: [PATCH 34/36] stub: rework linux handover to take "struct iovec" --- src/boot/efi/linux.c | 22 +++++++++------------- src/boot/efi/linux.h | 13 +++++-------- src/boot/efi/linux_x86.c | 40 +++++++++++++++++++++------------------- src/boot/efi/stub.c | 4 +--- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 65bc176df7..706648b49d 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -93,19 +93,17 @@ static EFI_STATUS load_image(EFI_HANDLE parent, const void *source, size_t len, EFI_STATUS linux_exec( EFI_HANDLE parent, const char16_t *cmdline, - const void *linux_buffer, - size_t linux_length, - const void *initrd_buffer, - size_t initrd_length) { + const struct iovec *kernel, + const struct iovec *initrd) { uint32_t compat_address; EFI_STATUS err; assert(parent); - assert(linux_buffer && linux_length > 0); - assert(initrd_buffer || initrd_length == 0); + assert(iovec_is_set(kernel)); + assert(iovec_is_valid(initrd)); - err = pe_kernel_info(linux_buffer, &compat_address); + err = pe_kernel_info(kernel->iov_base, &compat_address); #if defined(__i386__) || defined(__x86_64__) if (err == EFI_UNSUPPORTED) /* Kernel is too old to support LINUX_INITRD_MEDIA_GUID, try the deprecated EFI handover @@ -113,16 +111,14 @@ EFI_STATUS linux_exec( return linux_exec_efi_handover( parent, cmdline, - linux_buffer, - linux_length, - initrd_buffer, - initrd_length); + kernel, + initrd); #endif if (err != EFI_SUCCESS) return log_error_status(err, "Bad kernel image: %m"); _cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL; - err = load_image(parent, linux_buffer, linux_length, &kernel_image); + err = load_image(parent, kernel->iov_base, kernel->iov_len, &kernel_image); if (err != EFI_SUCCESS) return log_error_status(err, "Error loading kernel image: %m"); @@ -138,7 +134,7 @@ EFI_STATUS linux_exec( } _cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL; - err = initrd_register(initrd_buffer, initrd_length, &initrd_handle); + err = initrd_register(initrd->iov_base, initrd->iov_len, &initrd_handle); if (err != EFI_SUCCESS) return log_error_status(err, "Error registering initrd: %m"); diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 46b5f4f4d7..1bee8907c4 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -2,18 +2,15 @@ #pragma once #include "efi.h" +#include "iovec-util-fundamental.h" EFI_STATUS linux_exec( EFI_HANDLE parent, const char16_t *cmdline, - const void *linux_buffer, - size_t linux_length, - const void *initrd_buffer, - size_t initrd_length); + const struct iovec *kernel, + const struct iovec *initrd); EFI_STATUS linux_exec_efi_handover( EFI_HANDLE parent, const char16_t *cmdline, - const void *linux_buffer, - size_t linux_length, - const void *initrd_buffer, - size_t initrd_length); + const struct iovec *kernel, + const struct iovec *initrd); diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c index 757902daac..c456209831 100644 --- a/src/boot/efi/linux_x86.c +++ b/src/boot/efi/linux_x86.c @@ -123,19 +123,17 @@ static void linux_efi_handover(EFI_HANDLE parent, uintptr_t kernel, BootParams * EFI_STATUS linux_exec_efi_handover( EFI_HANDLE parent, const char16_t *cmdline, - const void *linux_buffer, - size_t linux_length, - const void *initrd_buffer, - size_t initrd_length) { + const struct iovec *kernel, + const struct iovec *initrd) { assert(parent); - assert(linux_buffer); - assert(initrd_buffer || initrd_length == 0); + assert(iovec_is_set(kernel)); + assert(iovec_is_valid(initrd)); - if (linux_length < sizeof(BootParams)) + if (kernel->iov_len < sizeof(BootParams)) return EFI_LOAD_ERROR; - const BootParams *image_params = (const BootParams *) linux_buffer; + const BootParams *image_params = (const BootParams *) kernel->iov_base; if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC) return log_error_status(EFI_UNSUPPORTED, "Unsupported kernel image."); if (image_params->hdr.version < SETUP_VERSION_2_11) @@ -155,22 +153,26 @@ EFI_STATUS linux_exec_efi_handover( /* There is no way to pass the high bits of code32_start. Newer kernels seems to handle this * just fine, but older kernels will fail even if they otherwise have above 4G boot support. */ _cleanup_pages_ Pages linux_relocated = {}; - if (POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX) { + const void *linux_buffer; + if (POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len > UINT32_MAX) { linux_relocated = xmalloc_pages( - AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(linux_length), UINT32_MAX); + AllocateMaxAddress, EfiLoaderCode, EFI_SIZE_TO_PAGES(kernel->iov_len), UINT32_MAX); linux_buffer = memcpy( - PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), linux_buffer, linux_length); - } + PHYSICAL_ADDRESS_TO_POINTER(linux_relocated.addr), kernel->iov_base, kernel->iov_len); + } else + linux_buffer = kernel->iov_base; _cleanup_pages_ Pages initrd_relocated = {}; - if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX) { + const void *initrd_buffer; + if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd->iov_base) + initrd->iov_len > UINT32_MAX) { initrd_relocated = xmalloc_pages( - AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(initrd_length), UINT32_MAX); + AllocateMaxAddress, EfiLoaderData, EFI_SIZE_TO_PAGES(initrd->iov_len), UINT32_MAX); initrd_buffer = memcpy( PHYSICAL_ADDRESS_TO_POINTER(initrd_relocated.addr), - initrd_buffer, - initrd_length); - } + initrd->iov_base, + initrd->iov_len); + } else + initrd_buffer = initrd->iov_base; _cleanup_pages_ Pages boot_params_page = xmalloc_pages( can_4g ? AllocateAnyPages : AllocateMaxAddress, @@ -215,8 +217,8 @@ EFI_STATUS linux_exec_efi_handover( boot_params->hdr.ramdisk_image = (uintptr_t) initrd_buffer; boot_params->ext_ramdisk_image = POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) >> 32; - boot_params->hdr.ramdisk_size = initrd_length; - boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32; + boot_params->hdr.ramdisk_size = initrd->iov_len; + boot_params->ext_ramdisk_size = ((uint64_t) initrd->iov_len) >> 32; log_wait(); linux_efi_handover(parent, (uintptr_t) linux_buffer, boot_params); diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 98d31af321..3e95eb1785 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -974,9 +974,7 @@ static EFI_STATUS run(EFI_HANDLE image) { (const uint8_t*) loaded_image->ImageBase + sections[UNIFIED_SECTION_LINUX].memory_offset, sections[UNIFIED_SECTION_LINUX].size); - err = linux_exec(image, cmdline, - kernel.iov_base, kernel.iov_len, - final_initrd.iov_base, final_initrd.iov_len); + err = linux_exec(image, cmdline, &kernel, &final_initrd); graphics_mode(false); return err; } From 75d08dc6e8e4e200cceed1ac904c307c665d1dd7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 15:05:33 +0200 Subject: [PATCH 35/36] measure: normalize error paths Always put the success path at least indentation, and indent the error paths. --- src/boot/efi/measure.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 24cb60e7a3..15f1ba9aff 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -195,12 +195,17 @@ static EFI_STATUS tcg2_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buf assert(ret_measured); tpm2 = tcg2_interface_check(); - if (tpm2) - err = tpm2_measure_to_pcr_and_ipl_event_log(tpm2, pcrindex, buffer, buffer_size, description); + if (!tpm2) { + *ret_measured = false; + return EFI_SUCCESS; + } - *ret_measured = tpm2 && (err == EFI_SUCCESS); + err = tpm2_measure_to_pcr_and_ipl_event_log(tpm2, pcrindex, buffer, buffer_size, description); + if (err != EFI_SUCCESS) + return err; - return err; + *ret_measured = true; + return EFI_SUCCESS; } static EFI_STATUS cc_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) { @@ -210,12 +215,17 @@ static EFI_STATUS cc_log_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, s assert(ret_measured); cc = cc_interface_check(); - if (cc) - err = cc_measure_to_mr_and_ipl_event_log(cc, pcrindex, buffer, buffer_size, description); + if (!cc) { + *ret_measured = false; + return EFI_SUCCESS; + } - *ret_measured = cc && (err == EFI_SUCCESS); + err = cc_measure_to_mr_and_ipl_event_log(cc, pcrindex, buffer, buffer_size, description); + if (err != EFI_SUCCESS) + return err; - return err; + *ret_measured = true; + return EFI_SUCCESS; } EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char16_t *description, bool *ret_measured) { @@ -240,10 +250,13 @@ EFI_STATUS tpm_log_ipl_event(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, siz return err; err = tcg2_log_ipl_event(pcrindex, buffer, buffer_size, description, &tpm_ret_measured); - if (err == EFI_SUCCESS && ret_measured) + if (err != EFI_SUCCESS) + return err; + + if (ret_measured) *ret_measured = tpm_ret_measured || cc_ret_measured; - return err; + return EFI_SUCCESS; } EFI_STATUS tpm_log_tagged_event( @@ -272,10 +285,11 @@ EFI_STATUS tpm_log_tagged_event( } err = tpm2_measure_to_pcr_and_tagged_event_log(tpm2, pcrindex, buffer, buffer_size, event_id, description); - if (err == EFI_SUCCESS && ret_measured) - *ret_measured = true; + if (!err) + return err; - return err; + *ret_measured = true; + return EFI_SUCCESS; } EFI_STATUS tpm_log_ipl_event_ascii(uint32_t pcrindex, EFI_PHYSICAL_ADDRESS buffer, size_t buffer_size, const char *description, bool *ret_measured) { From 506ffa0e72613542a1d96b4b98fa52f7f18cf7f8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Jun 2024 15:18:57 +0200 Subject: [PATCH 36/36] efi: share setting of generic efivars between sd-stub/sd-boot We have very similar code for setting generic efi vars in sd-stub and sd-boot. Let's share it. This changes behaviour in a minor way: if you chainload multiple versions of an sd-boot you'll see the efi vars of the first one now in the OS, not of the last one. But this should not matter, invocation like that should generally not happen. --- src/boot/efi/boot.c | 33 +++++++--------------------- src/boot/efi/export-vars.c | 44 ++++++++++++++++++++++++++++++++++++++ src/boot/efi/export-vars.h | 5 +++++ src/boot/efi/meson.build | 1 + src/boot/efi/stub.c | 40 ++++------------------------------ 5 files changed, 62 insertions(+), 61 deletions(-) create mode 100644 src/boot/efi/export-vars.c create mode 100644 src/boot/efi/export-vars.h diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 99061a1339..9dbf96f169 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -7,6 +7,7 @@ #include "devicetree.h" #include "drivers.h" #include "efivars-fundamental.h" +#include "export-vars.h" #include "graphics.h" #include "initrd.h" #include "linux.h" @@ -2538,9 +2539,8 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir) return EFI_SUCCESS; } -static void export_variables( +static void export_loader_variables( EFI_LOADED_IMAGE_PROTOCOL *loaded_image, - const char16_t *loaded_image_path, uint64_t init_usec) { static const uint64_t loader_features = @@ -2560,28 +2560,11 @@ static void export_variables( EFI_LOADER_FEATURE_MENU_DISABLE | 0; - _cleanup_free_ char16_t *infostr = NULL, *typestr = NULL; - assert(loaded_image); - efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeInitUSec", init_usec); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0); - - infostr = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", infostr, 0); - - typestr = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", typestr, 0); - + (void) efivar_set_time_usec(MAKE_GUID_PTR(LOADER), u"LoaderTimeInitUSec", init_usec); + (void) efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderInfo", u"systemd-boot " GIT_VERSION, 0); (void) efivar_set_uint64_le(MAKE_GUID_PTR(LOADER), u"LoaderFeatures", loader_features, 0); - - /* the filesystem path to this image, to prevent adding ourselves to the menu */ - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", loaded_image_path, 0); - - /* export the device path this image is started from */ - _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle); - if (uuid) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); } static void config_load_all_entries( @@ -2681,7 +2664,6 @@ static EFI_STATUS run(EFI_HANDLE image) { EFI_LOADED_IMAGE_PROTOCOL *loaded_image; _cleanup_(file_closep) EFI_FILE *root_dir = NULL; _cleanup_(config_free) Config config = {}; - _cleanup_free_ char16_t *loaded_image_path = NULL; EFI_STATUS err; uint64_t init_usec; bool menu = false; @@ -2696,9 +2678,8 @@ static EFI_STATUS run(EFI_HANDLE image) { if (err != EFI_SUCCESS) return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m"); - (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path); - - export_variables(loaded_image, loaded_image_path, init_usec); + export_common_variables(loaded_image); + export_loader_variables(loaded_image, init_usec); err = discover_root_dir(loaded_image, &root_dir); if (err != EFI_SUCCESS) @@ -2706,6 +2687,8 @@ static EFI_STATUS run(EFI_HANDLE image) { (void) load_drivers(image, loaded_image, root_dir); + _cleanup_free_ char16_t *loaded_image_path = NULL; + (void) device_path_to_str(loaded_image->FilePath, &loaded_image_path); config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir); if (config.n_entries == 0) diff --git a/src/boot/efi/export-vars.c b/src/boot/efi/export-vars.c new file mode 100644 index 0000000000..3926747bba --- /dev/null +++ b/src/boot/efi/export-vars.c @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "device-path-util.h" +#include "export-vars.h" +#include "part-discovery.h" +#include "util.h" + +void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { + assert(loaded_image); + + /* Export the device path this image is started from, if it's not set yet */ + if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) { + _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle); + if (uuid) + efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); + } + + /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the + * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note + * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us, + * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong + * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath + * is non-NULL explicitly.) */ + if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS && + loaded_image->FilePath) { + _cleanup_free_ char16_t *s = NULL; + if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS) + efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0); + } + + /* if LoaderFirmwareInfo is not set, let's set it */ + if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { + _cleanup_free_ char16_t *s = NULL; + s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); + efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0); + } + + /* ditto for LoaderFirmwareType */ + if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { + _cleanup_free_ char16_t *s = NULL; + s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); + efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); + } +} diff --git a/src/boot/efi/export-vars.h b/src/boot/efi/export-vars.h new file mode 100644 index 0000000000..a925d09827 --- /dev/null +++ b/src/boot/efi/export-vars.h @@ -0,0 +1,5 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "proto/loaded-image.h" + +void export_common_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 7a60b0ec7e..42a7914bf6 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -258,6 +258,7 @@ libefi_sources = files( 'devicetree.c', 'drivers.c', 'efi-string.c', + 'export-vars.c', 'graphics.c', 'initrd.c', 'log.c', diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 3e95eb1785..db4f473410 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -3,6 +3,7 @@ #include "cpio.h" #include "device-path-util.h" #include "devicetree.h" +#include "export-vars.h" #include "graphics.h" #include "iovec-util-fundamental.h" #include "linux.h" @@ -138,7 +139,7 @@ static EFI_STATUS combine_initrds( return EFI_SUCCESS; } -static void export_general_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { +static void export_stub_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { static const uint64_t stub_features = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION | /* We set LoaderDevicePartUUID */ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */ @@ -153,40 +154,6 @@ static void export_general_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) { assert(loaded_image); - /* Export the device path this image is started from, if it's not set yet */ - if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) { - _cleanup_free_ char16_t *uuid = disk_get_part_uuid(loaded_image->DeviceHandle); - if (uuid) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderDevicePartUUID", uuid, 0); - } - - /* If LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from the - * UEFI firmware without any boot loader, and hence set the LoaderImageIdentifier ourselves. Note - * that some boot chain loaders neither set LoaderImageIdentifier nor make FilePath available to us, - * in which case there's simple nothing to set for us. (The UEFI spec doesn't really say who's wrong - * here, i.e. whether FilePath may be NULL or not, hence handle this gracefully and check if FilePath - * is non-NULL explicitly.) */ - if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS && - loaded_image->FilePath) { - _cleanup_free_ char16_t *s = NULL; - if (device_path_to_str(loaded_image->FilePath, &s) == EFI_SUCCESS) - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderImageIdentifier", s, 0); - } - - /* if LoaderFirmwareInfo is not set, let's set it */ - if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { - _cleanup_free_ char16_t *s = NULL; - s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareInfo", s, 0); - } - - /* ditto for LoaderFirmwareType */ - if (efivar_get_raw(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { - _cleanup_free_ char16_t *s = NULL; - s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); - efivar_set(MAKE_GUID_PTR(LOADER), u"LoaderFirmwareType", s, 0); - } - /* add StubInfo (this is one is owned by the stub, hence we unconditionally override this with our * own data) */ (void) efivar_set(MAKE_GUID_PTR(LOADER), u"StubInfo", u"systemd-stub " GIT_VERSION, 0); @@ -939,7 +906,8 @@ static EFI_STATUS run(EFI_HANDLE image) { cmdline_append_and_measure_addons(cmdline_addons, &cmdline, ¶meters_measured); cmdline_append_and_measure_smbios(&cmdline, ¶meters_measured); - export_general_variables(loaded_image); + export_common_variables(loaded_image); + export_stub_variables(loaded_image); /* First load the base device tree, then fix it up using addons - global first, then per-UKI. */ install_embedded_devicetree(loaded_image, sections, &dt_state);