From 1328150d854ebc0214054b817b017594c70bea89 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 13 Aug 2021 18:55:32 +0200 Subject: [PATCH 1/4] sd-boot: Fix PE section parsing We only need the PE header offset from the DOS header, not its size. Previously, the section table could be cut off in the middle. While we are at it, also modernize the remaining code. --- src/basic/macro.h | 1 - src/boot/efi/boot.c | 5 +- src/boot/efi/linux.h | 5 +- src/boot/efi/measure.c | 4 +- src/boot/efi/missing_efi.h | 4 + src/boot/efi/pe.c | 207 ++++++++++++++++------------ src/boot/efi/pe.h | 18 ++- src/boot/efi/splash.c | 6 +- src/boot/efi/stub.c | 5 +- src/fundamental/macro-fundamental.h | 1 + 10 files changed, 152 insertions(+), 104 deletions(-) diff --git a/src/basic/macro.h b/src/basic/macro.h index 645f93096f..280f302864 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -20,7 +20,6 @@ #define _sentinel_ __attribute__((__sentinel__)) #define _destructor_ __attribute__((__destructor__)) #define _deprecated_ __attribute__((__deprecated__)) -#define _packed_ __attribute__((__packed__)) #define _malloc_ __attribute__((__malloc__)) #define _weak_ __attribute__((__weak__)) #define _public_ __attribute__((__visibility__("default"))) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 638070d77a..9a81af42ac 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1897,14 +1897,13 @@ static VOID config_entry_add_linux( CHAR16 buf[256]; UINTN bufsize = sizeof buf; EFI_FILE_INFO *f; - CHAR8 *sections[] = { + const CHAR8 *sections[] = { (CHAR8 *)".osrel", (CHAR8 *)".cmdline", NULL }; UINTN offs[ELEMENTSOF(sections)-1] = {}; UINTN szs[ELEMENTSOF(sections)-1] = {}; - UINTN addrs[ELEMENTSOF(sections)-1] = {}; CHAR8 *content = NULL; CHAR8 *line; UINTN pos = 0; @@ -1931,7 +1930,7 @@ static VOID config_entry_add_linux( continue; /* look for .osrel and .cmdline sections in the .efi binary */ - err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs); + err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs); if (EFI_ERROR(err)) continue; diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 09be2de27b..7fdef9990b 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -2,6 +2,7 @@ #pragma once #include +#include "macro-fundamental.h" #define SETUP_MAGIC 0x53726448 /* "HdrS" */ @@ -44,7 +45,7 @@ struct setup_header { UINT64 pref_address; UINT32 init_size; UINT32 handover_offset; -} __attribute__((packed)); +} _packed_; /* adapted from linux' bootparam.h */ struct boot_params { @@ -81,7 +82,7 @@ struct boot_params { UINT8 _pad8[48]; UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR] UINT8 _pad9[276]; -} __attribute__((packed)); +} _packed_; EFI_STATUS linux_exec(EFI_HANDLE *image, CHAR8 *cmdline, UINTN cmdline_size, diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 429e82657b..1d19366f73 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -133,13 +133,13 @@ typedef struct { UINT16 HeaderVersion; UINT32 PCRIndex; UINT32 EventType; -} __attribute__((packed)) EFI_TCG2_EVENT_HEADER; +} _packed_ EFI_TCG2_EVENT_HEADER; typedef struct tdEFI_TCG2_EVENT { UINT32 Size; EFI_TCG2_EVENT_HEADER Header; UINT8 Event[1]; -} __attribute__((packed)) EFI_TCG2_EVENT; +} _packed_ EFI_TCG2_EVENT; typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This, IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability); diff --git a/src/boot/efi/missing_efi.h b/src/boot/efi/missing_efi.h index b983931348..37c468f2ab 100644 --- a/src/boot/efi/missing_efi.h +++ b/src/boot/efi/missing_efi.h @@ -120,3 +120,7 @@ typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL { } EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL; #endif + +#ifndef EFI_IMAGE_MACHINE_RISCV64 + #define EFI_IMAGE_MACHINE_RISCV64 0x5064 +#endif diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 5c20acc9f3..04b4504c2e 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -6,6 +6,24 @@ #include "pe.h" #include "util.h" +#define DOS_FILE_MAGIC "MZ" +#define PE_FILE_MAGIC "PE\0\0" +#define MAX_SECTIONS 96 + +#if defined(__i386__) + #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32 +#elif defined(__x86_64__) + #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64 +#elif defined(__aarch64__) + #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64 +#elif defined(__arm__) + #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED +#elif defined(__riscv) && __riscv_xlen == 64 + #define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64 +#else + #error Unknown EFI arch +#endif + struct DosFileHeader { UINT8 Magic[2]; UINT16 LastSize; @@ -26,14 +44,9 @@ struct DosFileHeader { UINT16 OEMInfo; UINT16 reserved2[10]; UINT32 ExeHeader; -} __attribute__((packed)); +} _packed_; -#define PE_HEADER_MACHINE_I386 0x014c -#define PE_HEADER_MACHINE_X64 0x8664 -#define PE_HEADER_MACHINE_ARM64 0xaa64 -#define PE_HEADER_MACHINE_ARM 0x01c2 -#define PE_HEADER_MACHINE_RISCV64 0x5064 -struct PeFileHeader { +struct CoffFileHeader { UINT16 Machine; UINT16 NumberOfSections; UINT32 TimeDateStamp; @@ -41,12 +54,13 @@ struct PeFileHeader { UINT32 NumberOfSymbols; UINT16 SizeOfOptionalHeader; UINT16 Characteristics; -} __attribute__((packed)); +} _packed_; -struct PeHeader { +struct PeFileHeader { UINT8 Magic[4]; - struct PeFileHeader FileHeader; -} __attribute__((packed)); + struct CoffFileHeader FileHeader; + /* OptionalHeader omitted */ +} _packed_; struct PeSectionHeader { UINT8 Name[8]; @@ -59,120 +73,143 @@ struct PeSectionHeader { UINT16 NumberOfRelocations; UINT16 NumberOfLinenumbers; UINT32 Characteristics; -} __attribute__((packed)); +} _packed_; -EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { - struct DosFileHeader *dos; - struct PeHeader *pe; - UINTN offset; +static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) { + assert(dos); + return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0; +} - assert(base); +static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) { + assert(pe); + return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 && + pe->FileHeader.Machine == TARGET_MACHINE_TYPE && + pe->FileHeader.NumberOfSections > 0 && + pe->FileHeader.NumberOfSections <= MAX_SECTIONS; +} + +static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) { + assert(dos); + assert(pe); + return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader; +} + +static VOID locate_sections( + const struct PeSectionHeader section_table[], + UINTN n_table, + const CHAR8 **sections, + UINTN *addrs, + UINTN *offsets, + UINTN *sizes) { + + assert(section_table); assert(sections); + assert(sizes); - dos = (struct DosFileHeader *)base; + for (UINTN i = 0; i < n_table; i++) { + const struct PeSectionHeader *sect = section_table + i; - if (CompareMem(dos->Magic, "MZ", 2) != 0) - return EFI_LOAD_ERROR; - - pe = (struct PeHeader *)&base[dos->ExeHeader]; - if (CompareMem(pe->Magic, "PE\0\0", 4) != 0) - return EFI_LOAD_ERROR; - - /* PE32+ Subsystem type */ - if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 && - pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64 && - pe->FileHeader.Machine != PE_HEADER_MACHINE_I386 && - pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM && - pe->FileHeader.Machine != PE_HEADER_MACHINE_RISCV64) - return EFI_LOAD_ERROR; - - if (pe->FileHeader.NumberOfSections > 96) - return EFI_LOAD_ERROR; - - offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader; - - for (UINTN i = 0; i < pe->FileHeader.NumberOfSections; i++) { - struct PeSectionHeader *sect; - - sect = (struct PeSectionHeader *)&base[offset]; for (UINTN j = 0; sections[j]; j++) { if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0) continue; if (addrs) - addrs[j] = (UINTN)sect->VirtualAddress; + addrs[j] = sect->VirtualAddress; if (offsets) - offsets[j] = (UINTN)sect->PointerToRawData; - if (sizes) - sizes[j] = (UINTN)sect->VirtualSize; + offsets[j] = sect->PointerToRawData; + sizes[j] = sect->VirtualSize; } - offset += sizeof(*sect); } +} + +EFI_STATUS pe_memory_locate_sections( + const CHAR8 *base, + const CHAR8 **sections, + UINTN *addrs, + UINTN *sizes) { + const struct DosFileHeader *dos; + const struct PeFileHeader *pe; + UINTN offset; + + assert(base); + assert(sections); + assert(addrs); + assert(sizes); + + dos = (const struct DosFileHeader*)base; + if (!verify_dos(dos)) + return EFI_LOAD_ERROR; + + pe = (const struct PeFileHeader*)&base[dos->ExeHeader]; + if (!verify_pe(pe)) + return EFI_LOAD_ERROR; + + offset = section_table_offset(dos, pe); + locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections, + sections, addrs, NULL, sizes); return EFI_SUCCESS; } -EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) { - EFI_FILE_HANDLE handle; +EFI_STATUS pe_file_locate_sections( + EFI_FILE *dir, + const CHAR16 *path, + const CHAR8 **sections, + UINTN *offsets, + UINTN *sizes) { + _cleanup_freepool_ struct PeSectionHeader *section_table = NULL; + _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; struct DosFileHeader dos; - struct PeHeader pe; - UINTN len; - UINTN headerlen; + struct PeFileHeader pe; + UINTN len, section_table_len; EFI_STATUS err; - _cleanup_freepool_ CHAR8 *header = NULL; assert(dir); assert(path); + assert(sections); + assert(offsets); + assert(sizes); - err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL); + err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) return err; - /* MS-DOS stub */ len = sizeof(dos); err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos); if (EFI_ERROR(err)) - goto out; - if (len != sizeof(dos)) { - err = EFI_LOAD_ERROR; - goto out; - } + return err; + if (len != sizeof(dos) || !verify_dos(&dos)) + return EFI_LOAD_ERROR; err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader); if (EFI_ERROR(err)) - goto out; + return err; len = sizeof(pe); err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe); if (EFI_ERROR(err)) - goto out; - if (len != sizeof(pe)) { - err = EFI_LOAD_ERROR; - goto out; - } + return err; + if (len != sizeof(pe) || !verify_pe(&pe)) + return EFI_LOAD_ERROR; - headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader); - header = AllocatePool(headerlen); - if (!header) { - err = EFI_OUT_OF_RESOURCES; - goto out; - } - len = headerlen; - err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0); + section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader); + section_table = AllocatePool(section_table_len); + if (!section_table) + return EFI_OUT_OF_RESOURCES; + + err = uefi_call_wrapper(handle->SetPosition, 2, handle, section_table_offset(&dos, &pe)); if (EFI_ERROR(err)) - goto out; + return err; - err = uefi_call_wrapper(handle->Read, 3, handle, &len, header); + len = section_table_len; + err = uefi_call_wrapper(handle->Read, 3, handle, &len, section_table); if (EFI_ERROR(err)) - goto out; + return err; + if (len != section_table_len) + return EFI_LOAD_ERROR; - if (len != headerlen) { - err = EFI_LOAD_ERROR; - goto out; - } + locate_sections(section_table, pe.FileHeader.NumberOfSections, + sections, NULL, offsets, sizes); - err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes); -out: - uefi_call_wrapper(handle->Close, 1, handle); - return err; + return EFI_SUCCESS; } diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h index 06a0fcd70c..e355211223 100644 --- a/src/boot/efi/pe.h +++ b/src/boot/efi/pe.h @@ -1,9 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include +#include -EFI_STATUS pe_memory_locate_sections(CHAR8 *base, - CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); -EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, - CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes); +EFI_STATUS pe_memory_locate_sections( + const CHAR8 *base, + const CHAR8 **sections, + UINTN *addrs, + UINTN *sizes); + +EFI_STATUS pe_file_locate_sections( + EFI_FILE *dir, + const CHAR16 *path, + const CHAR8 **sections, + UINTN *offsets, + UINTN *sizes); diff --git a/src/boot/efi/splash.c b/src/boot/efi/splash.c index 920b440825..23627a794e 100644 --- a/src/boot/efi/splash.c +++ b/src/boot/efi/splash.c @@ -12,7 +12,7 @@ struct bmp_file { UINT32 size; UINT16 reserved[2]; UINT32 offset; -} __attribute__((packed)); +} _packed_; /* we require at least BITMAPINFOHEADER, later versions are accepted, but their features ignored */ @@ -28,14 +28,14 @@ struct bmp_dib { INT32 y_pixel_meter; UINT32 colors_used; UINT32 colors_important; -} __attribute__((packed)); +} _packed_; struct bmp_map { UINT8 blue; UINT8 green; UINT8 red; UINT8 reserved; -} __attribute__((packed)); +} _packed_; static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib, struct bmp_map **ret_map, UINT8 **pixmap) { diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 82da1d3ec4..5758b5cd94 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -17,7 +17,7 @@ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_LOADED_IMAGE *loaded_image; - CHAR8 *sections[] = { + const CHAR8 *sections[] = { (CHAR8 *)".cmdline", (CHAR8 *)".linux", (CHAR8 *)".initrd", @@ -25,7 +25,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { NULL }; UINTN addrs[ELEMENTSOF(sections)-1] = {}; - UINTN offs[ELEMENTSOF(sections)-1] = {}; UINTN szs[ELEMENTSOF(sections)-1] = {}; CHAR8 *cmdline = NULL; UINTN cmdline_len; @@ -39,7 +38,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { if (EFI_ERROR(err)) return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err); - err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs); + err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs); if (EFI_ERROR(err)) return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err); diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 461d37fa9e..20d8dabf29 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -11,6 +11,7 @@ #define _const_ __attribute__((__const__)) #define _pure_ __attribute__((__pure__)) #define _section_(x) __attribute__((__section__(x))) +#define _packed_ __attribute__((__packed__)) #define _used_ __attribute__((__used__)) #define _unused_ __attribute__((__unused__)) #define _cleanup_(x) __attribute__((__cleanup__(x))) From 12450f2e77bef0244051a75c6a1cbd9ba2d732ae Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 13 Aug 2021 19:03:35 +0200 Subject: [PATCH 2/4] sd-boot: Try harder to detect ourselves By moving our magic string into its own PE section, we can forego grepping for it. --- src/boot/efi/boot.c | 49 ++++++++++++++++++++++++++++------------ src/boot/efi/meson.build | 1 + 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 9a81af42ac..70f5a2ba52 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -22,7 +22,7 @@ #endif /* magic string to find in the binary image */ -static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####"; +static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####"; enum loader_type { LOADER_UNDEFINED, @@ -1792,6 +1792,29 @@ static ConfigEntry *config_entry_add_loader( return entry; } +static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) { + EFI_STATUS err; + const CHAR8 *sections[] = { + (CHAR8 *)".sdmagic", + NULL + }; + UINTN offset = 0, size = 0, read; + _cleanup_freepool_ CHAR8 *content = NULL; + + assert(root_dir); + assert(loader_path); + + err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size); + if (EFI_ERROR(err) || size != sizeof(magic)) + return FALSE; + + err = file_read(root_dir, loader_path, offset, size, &content, &read); + if (EFI_ERROR(err) || size != read) + return FALSE; + + return CompareMem(content, magic, sizeof(magic)) == 0; +} + static BOOLEAN config_entry_add_loader_auto( Config *config, EFI_HANDLE *device, @@ -1811,25 +1834,23 @@ static BOOLEAN config_entry_add_loader_auto( assert(root_dir); assert(id); assert(title); - assert(loader); + assert(loader || loaded_image_path); if (!config->auto_entries) return FALSE; - /* do not add an entry for ourselves */ if (loaded_image_path) { - UINTN len; - _cleanup_freepool_ CHAR8 *content = NULL; + loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi"; - if (StriCmp(loader, loaded_image_path) == 0) + /* We are trying to add the default EFI loader here, + * but we do not want to do that if that would be us. + * + * If the default loader is not us, it might be shim. It would + * chainload GRUBX64.EFI in that case, which might be us.*/ + if (StriCmp(loader, loaded_image_path) == 0 || + is_sd_boot(root_dir, loader) || + is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI")) return FALSE; - - /* look for systemd-boot magic string */ - err = file_read(root_dir, loader, 0, 100*1024, &content, &len); - if (!EFI_ERROR(err)) - for (CHAR8 *start = content; start <= content + len - sizeof(magic) - 1; start++) - if (start[0] == magic[0] && CompareMem(start, magic, sizeof(magic) - 1) == 0) - return FALSE; } /* check existence */ @@ -2414,7 +2435,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL, L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, - L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi"); + L"auto-efi-default", '\0', L"EFI Default Loader", NULL); config_entry_add_osx(&config); if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) { diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index c96a16426a..3d16ce84b5 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -292,6 +292,7 @@ if have_gnu_efi '-j', '.text', '-j', '.sdata', '-j', '.sbat', + '-j', '.sdmagic', '-j', '.data', '-j', '.dynamic', '-j', '.dynsym', From 43ee1fe0868ff611636c3aca7746cec80bc3c448 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 13 Aug 2021 19:03:35 +0200 Subject: [PATCH 3/4] sd-boot: Add memmem_safe and memory_startswith --- src/basic/string-util.h | 16 ---------------- src/boot/efi/util.c | 14 ++++++++++++++ src/boot/efi/util.h | 7 +++++++ src/fundamental/string-util-fundamental.h | 17 +++++++++++++++++ 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 9155e50ba8..5c11adf393 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) { } REENABLE_WARNING; -/* Like startswith(), but operates on arbitrary memory blocks */ -static inline void *memory_startswith(const void *p, size_t sz, const char *token) { - assert(token); - - size_t n = strlen(token); - if (sz < n) - return NULL; - - assert(p); - - if (memcmp(p, token, n) != 0) - return NULL; - - return (uint8_t*) p + n; -} - /* Like startswith_no_case(), but operates on arbitrary memory blocks. * It works only for ASCII strings. */ diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 0c28cab246..823689e223 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -501,3 +501,17 @@ EFI_STATUS log_oom(void) { log_error_stall(L"Out of memory."); return EFI_OUT_OF_RESOURCES; } + +VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) { + assert(haystack || haystack_len == 0); + assert(needle || needle_len == 0); + + if (needle_len == 0) + return (VOID*)haystack; + + for (const CHAR8 *h = haystack, *n = needle; haystack_len >= needle_len; h++, haystack_len--) + if (*h == *n && CompareMem(h + 1, n + 1, needle_len - 1) == 0) + return (VOID*)h; + + return NULL; +} diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index d3bf848a95..f620f14212 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -84,3 +84,10 @@ EFI_STATUS log_oom(void); log_error_stall(fmt, ##__VA_ARGS__); \ err; \ }) + +VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len); + +static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) { + CHAR8 *p = memmem_safe(haystack, haystack_len, needle, needle_len); + return p ? p + needle_len : NULL; +} diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h index 407cede48d..7455c05492 100644 --- a/src/fundamental/string-util-fundamental.h +++ b/src/fundamental/string-util-fundamental.h @@ -16,6 +16,7 @@ #define strncmp(a, b, n) StrnCmp((a), (b), (n)) #define strcasecmp(a, b) StriCmp((a), (b)) #define STR_C(str) (L ## str) +#define memcmp(a, b, n) CompareMem(a, b, n) #else #define STR_C(str) (str) #endif @@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) { } sd_int strverscmp_improved(const sd_char *a, const sd_char *b); + +/* Like startswith(), but operates on arbitrary memory blocks */ +static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) { + assert(token); + + sd_size_t n = strlen(token) * sizeof(sd_char); + if (sz < n) + return NULL; + + assert(p); + + if (memcmp(p, token, n) != 0) + return NULL; + + return (uint8_t*) p + n; +} From 4a59f399c995086433754900360ee9ea593293a5 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Fri, 13 Aug 2021 19:03:35 +0200 Subject: [PATCH 4/4] sd-boot: Detect windows boot loader title from BCD --- src/boot/efi/boot.c | 54 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 70f5a2ba52..82da984325 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1897,6 +1897,55 @@ static VOID config_entry_add_osx(Config *config) { } } +static VOID config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) { + _cleanup_freepool_ CHAR8 *bcd = NULL; + const CHAR16 *title = NULL; + EFI_STATUS err; + UINTN len; + + assert(config); + assert(device); + assert(root_dir); + + if (!config->auto_entries) + return; + + /* Try to find a better title. */ + err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len); + if (!EFI_ERROR(err)) { + static const CHAR16 *versions[] = { + L"Windows 11", + L"Windows 10", + L"Windows 8.1", + L"Windows 8", + L"Windows 7", + L"Windows Vista", + }; + + CHAR8 *p = bcd; + while (!title) { + CHAR8 *q = mempmem_safe(p, len, versions[0], STRLEN(L"Windows ")); + if (!q) + break; + + len -= q - p; + p = q; + + /* We found the prefix, now try all the version strings. */ + for (UINTN i = 0; i < ELEMENTSOF(versions); i++) { + if (memory_startswith(p, len, versions[i] + STRLEN("Windows "))) { + title = versions[i]; + break; + } + } + } + } + + config_entry_add_loader_auto(config, device, root_dir, NULL, + L"auto-windows", 'w', title ?: L"Windows Boot Manager", + L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); +} + static VOID config_entry_add_linux( Config *config, EFI_HANDLE *device, @@ -2430,13 +2479,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { config_sort_entries(&config); /* if we find some well-known loaders, add them to the end of the list */ - config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL, - L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + config_entry_add_osx(&config); + config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir); config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL, L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi"); config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path, L"auto-efi-default", '\0', L"EFI Default Loader", NULL); - config_entry_add_osx(&config); if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) { if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)