diff --git a/meson.build b/meson.build index 4c0dd6f8c9..e77c2aa3ac 100644 --- a/meson.build +++ b/meson.build @@ -1676,8 +1676,14 @@ conf.set10('ENABLE_TIMEDATECTL', get_option('timedated') or get_option('timesync conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests) -##################################################################### +############################################################ +tests = [] +fuzzers = [] + +############################################################ + +# Include these now as they provide gnu-efi detection. subdir('src/fundamental') subdir('src/boot/efi') @@ -1695,7 +1701,7 @@ update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh') xml_helper_py = find_program('tools/xml_helper.py') export_dbus_interfaces_py = find_program('tools/dbus_exporter.py') -##################################################################### +############################################################ config_h = configure_file( output : 'config.h', @@ -1716,9 +1722,6 @@ if dbus_interfaces_dir == '' dbus_interfaces_dir = get_option('datadir') + '/dbus-1' endif -tests = [] -fuzzers = [] - basic_includes = include_directories( 'src/basic', 'src/fundamental', diff --git a/src/boot/efi/bcd.c b/src/boot/efi/bcd.c new file mode 100644 index 0000000000..970c8b1c8e --- /dev/null +++ b/src/boot/efi/bcd.c @@ -0,0 +1,321 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#ifdef SD_BOOT +# include +# include "macro-fundamental.h" +# include "util.h" +# define TEST_STATIC +#else +/* Provide our own "EFI API" if we are running as a unit test. */ +# include +# include +# include +# include "string-util-fundamental.h" + +# define CHAR8 char +# define CHAR16 char16_t +# define UINT8 uint8_t +# define UINT16 uint16_t +# define UINT32 uint32_t +# define UINT64 uint64_t +# define UINTN size_t +# define strncaseeqa(a, b, n) strncaseeq((a), (b), (n)) +# define TEST_STATIC static +#endif + +enum { + SIG_BASE_BLOCK = 1718052210, /* regf */ + SIG_KEY = 27502, /* nk */ + SIG_SUBKEY_FAST = 26220, /* lf */ + SIG_KEY_VALUE = 27510, /* vk */ +}; + +enum { + REG_SZ = 1, + REG_MULTI_SZ = 7, +}; + +/* These structs contain a lot more members than we care for. They have all + * been squashed into _padN for our convenience. */ + +typedef struct { + UINT32 sig; + UINT32 primary_seqnum; + UINT32 secondary_seqnum; + UINT64 _pad1; + UINT32 version_major; + UINT32 version_minor; + UINT32 type; + UINT32 _pad2; + UINT32 root_cell_offset; + UINT64 _pad3[507]; +} _packed_ BaseBlock; +assert_cc(sizeof(BaseBlock) == 4096); +assert_cc(offsetof(BaseBlock, sig) == 0); +assert_cc(offsetof(BaseBlock, primary_seqnum) == 4); +assert_cc(offsetof(BaseBlock, secondary_seqnum) == 8); +assert_cc(offsetof(BaseBlock, version_major) == 20); +assert_cc(offsetof(BaseBlock, version_minor) == 24); +assert_cc(offsetof(BaseBlock, type) == 28); +assert_cc(offsetof(BaseBlock, root_cell_offset) == 36); + +/* All offsets are relative to the base block and technically point to a hive + * cell struct. But for our usecase we don't need to bother about that one, + * so skip over the cell_size UINT32. */ +#define HIVE_CELL_OFFSET (sizeof(BaseBlock) + 4) + +typedef struct { + UINT16 sig; + UINT16 _pad1[13]; + UINT32 subkeys_offset; + UINT32 _pad2; + UINT32 n_key_values; + UINT32 key_values_offset; + UINT32 _pad3[7]; + UINT16 key_name_len; + UINT16 _pad4; + CHAR8 key_name[]; +} _packed_ Key; +assert_cc(offsetof(Key, sig) == 0); +assert_cc(offsetof(Key, subkeys_offset) == 28); +assert_cc(offsetof(Key, n_key_values) == 36); +assert_cc(offsetof(Key, key_values_offset) == 40); +assert_cc(offsetof(Key, key_name_len) == 72); +assert_cc(offsetof(Key, key_name) == 76); + +typedef struct { + UINT16 sig; + UINT16 n_entries; + struct SubkeyFastEntry { + UINT32 key_offset; + CHAR8 name_hint[4]; + } _packed_ entries[]; +} _packed_ SubkeyFast; +assert_cc(offsetof(SubkeyFast, sig) == 0); +assert_cc(offsetof(SubkeyFast, n_entries) == 2); +assert_cc(offsetof(SubkeyFast, entries) == 4); + +typedef struct { + UINT16 sig; + UINT16 name_len; + UINT32 data_size; + UINT32 data_offset; + UINT32 data_type; + UINT32 _pad; + CHAR8 name[]; +} _packed_ KeyValue; +assert_cc(offsetof(KeyValue, sig) == 0); +assert_cc(offsetof(KeyValue, name_len) == 2); +assert_cc(offsetof(KeyValue, data_size) == 4); +assert_cc(offsetof(KeyValue, data_offset) == 8); +assert_cc(offsetof(KeyValue, data_type) == 12); +assert_cc(offsetof(KeyValue, name) == 20); + +static const Key *get_key(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const CHAR8 *name); + +static const Key *get_subkey(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const CHAR8 *name) { + assert(bcd); + assert(name); + + if ((UINT64) offset + sizeof(SubkeyFast) > bcd_len) + return NULL; + + const SubkeyFast *subkey = (const SubkeyFast *) (bcd + offset); + if (subkey->sig != SIG_SUBKEY_FAST) + return NULL; + + if ((UINT64) offset + offsetof(SubkeyFast, entries) + sizeof(struct SubkeyFastEntry[subkey->n_entries]) > bcd_len) + return NULL; + + for (UINT16 i = 0; i < subkey->n_entries; i++) { + if (!strncaseeqa(name, subkey->entries[i].name_hint, sizeof(subkey->entries[i].name_hint))) + continue; + + const Key *key = get_key(bcd, bcd_len, subkey->entries[i].key_offset, name); + if (key) + return key; + } + + return NULL; +} + +/* We use NUL as registry path separators for convenience. To start from the root, begin + * name with a NUL. Name must end with two NUL. The lookup depth is not restricted, so + * name must be properly validated before calling get_key(). */ +static const Key *get_key(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const CHAR8 *name) { + assert(bcd); + assert(name); + + if ((UINT64) offset + sizeof(Key) > bcd_len) + return NULL; + + const Key *key = (const Key *) (bcd + offset); + if (key->sig != SIG_KEY) + return NULL; + + if ((UINT64) offset + offsetof(Key, key_name) + sizeof(CHAR8[key->key_name_len]) > bcd_len) + return NULL; + + if (*name) { + if (strncaseeqa(name, key->key_name, key->key_name_len) && !name[key->key_name_len]) + name += key->key_name_len; + else + return NULL; + } + + name++; + return *name ? get_subkey(bcd, bcd_len, key->subkeys_offset, name) : key; +} + +static const KeyValue *get_key_value(const UINT8 *bcd, UINT32 bcd_len, const Key *key, const CHAR8 *name) { + assert(bcd); + assert(key); + assert(name); + + if (key->n_key_values == 0) + return NULL; + + if ((UINT64) key->key_values_offset + sizeof(UINT32[key->n_key_values]) > bcd_len) + return NULL; + + const UINT32 *key_value_list = (const UINT32 *) (bcd + key->key_values_offset); + for (UINT32 i = 0; i < key->n_key_values; i++) { + UINT32 offset = *(key_value_list + i); + + if ((UINT64) offset + sizeof(KeyValue) > bcd_len) + continue; + + const KeyValue *kv = (const KeyValue *) (bcd + offset); + if (kv->sig != SIG_KEY_VALUE) + continue; + + if ((UINT64) offset + offsetof(KeyValue, name) + kv->name_len > bcd_len) + continue; + + /* If most significant bit is set, data is stored in data_offset itself, but + * we are only interested in UTF16 strings. The only strings that could fit + * would have just one char in it, so let's not bother with this. */ + if (FLAGS_SET(kv->data_size, UINT32_C(1) << 31)) + continue; + + if ((UINT64) kv->data_offset + kv->data_size > bcd_len) + continue; + + if (strncaseeqa(name, kv->name, kv->name_len) && !name[kv->name_len]) + return kv; + } + + return NULL; +} + +/* The BCD store is really just a regular windows registry hive with a rather cryptic internal + * key structure. On a running system it gets mounted to HKEY_LOCAL_MACHINE\BCD00000000. + * + * Of interest to us are the these two keys: + * - \Objects\{bootmgr}\Elements\24000001 + * This key is the "displayorder" property and contains a value of type REG_MULTI_SZ + * with the name "Element" that holds a {GUID} list (UTF16, NUL-separated). + * - \Objects\{GUID}\Elements\12000004 + * This key is the "description" property and contains a value of type REG_SZ with the + * name "Element" that holds a NUL-terminated UTF16 string. + * + * The GUIDs and properties are as reported by "bcdedit.exe /v". + * + * To get a title for the BCD store we first look at the displayorder property of {bootmgr} + * (it always has the GUID 9dea862c-5cdd-4e70-acc1-f32b344d4795). If it contains more than + * one GUID, the BCD is multi-boot and we stop looking. Otherwise we take that GUID, look it + * up, and return its description property. */ +TEST_STATIC CHAR16 *get_bcd_title(UINT8 *bcd, UINTN bcd_len) { + assert(bcd); + + if (HIVE_CELL_OFFSET > bcd_len) + return NULL; + + BaseBlock *base_block = (BaseBlock *) bcd; + if (base_block->sig != SIG_BASE_BLOCK || + base_block->version_major != 1 || + base_block->version_minor != 3 || + base_block->type != 0 || + base_block->primary_seqnum != base_block->secondary_seqnum) + return NULL; + + bcd += HIVE_CELL_OFFSET; + bcd_len -= HIVE_CELL_OFFSET; + + const Key *objects_key = get_key( + bcd, bcd_len, + base_block->root_cell_offset, + (const CHAR8 *) "\0Objects\0"); + if (!objects_key) + return NULL; + + const Key *displayorder_key = get_subkey( + bcd, bcd_len, + objects_key->subkeys_offset, + (const CHAR8 *) "{9dea862c-5cdd-4e70-acc1-f32b344d4795}\0Elements\00024000001\0"); + if (!displayorder_key) + return NULL; + + const KeyValue *displayorder_value = get_key_value( + bcd, bcd_len, + displayorder_key, + (const CHAR8 *) "Element"); + if (!displayorder_value) + return NULL; + + CHAR8 order_guid[sizeof("{00000000-0000-0000-0000-000000000000}\0")]; + if (displayorder_value->data_type != REG_MULTI_SZ || + displayorder_value->data_size != sizeof(CHAR16) * sizeof(order_guid)) + /* BCD is multi-boot. */ + return NULL; + + /* Keys are stored as ASCII in registry hives if the data fits (and GUIDS always should). */ + CHAR16 *order_guid_utf16 = (CHAR16 *) (bcd + displayorder_value->data_offset); + for (UINTN i = 0; i < sizeof(order_guid) - 2; i++) { + CHAR16 c = order_guid_utf16[i]; + switch (c) { + case '-': + case '{': + case '}': + case '0' ... '9': + case 'a' ... 'f': + case 'A' ... 'F': + order_guid[i] = c; + break; + default: + /* Not a valid GUID. */ + return NULL; + } + } + /* Our functions expect the lookup key to be double-derminated. */ + order_guid[sizeof(order_guid) - 2] = '\0'; + order_guid[sizeof(order_guid) - 1] = '\0'; + + const Key *default_key = get_subkey(bcd, bcd_len, objects_key->subkeys_offset, order_guid); + if (!default_key) + return NULL; + + const Key *description_key = get_subkey( + bcd, bcd_len, + default_key->subkeys_offset, + (const CHAR8 *) "Elements\00012000004\0"); + if (!description_key) + return NULL; + + const KeyValue *description_value = get_key_value( + bcd, bcd_len, + description_key, + (const CHAR8 *) "Element"); + if (!description_value) + return NULL; + + if (description_value->data_type != REG_SZ || + description_value->data_size < sizeof(CHAR16) || + description_value->data_size % sizeof(CHAR16) != 0) + return NULL; + + /* The data should already be NUL-terminated. */ + CHAR16 *title = (CHAR16 *) (bcd + description_value->data_offset); + title[description_value->data_size / sizeof(CHAR16)] = '\0'; + return title; +} diff --git a/src/boot/efi/bcd.h b/src/boot/efi/bcd.h new file mode 100644 index 0000000000..e55565acba --- /dev/null +++ b/src/boot/efi/bcd.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +CHAR16 *get_bcd_title(UINT8 *bcd, UINTN bcd_len); diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 23f9980315..17d14a5dac 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -4,6 +4,7 @@ #include #include +#include "bcd.h" #include "bootspec-fundamental.h" #include "console.h" #include "devicetree.h" @@ -1941,7 +1942,7 @@ 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; + CHAR16 *title = NULL; EFI_STATUS err; UINTN len; @@ -1954,34 +1955,8 @@ static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FIL /* 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; - } - } - } - } + if (!EFI_ERROR(err)) + title = get_bcd_title((UINT8 *) bcd, len); config_entry_add_loader_auto(config, device, root_dir, NULL, L"auto-windows", 'w', title ?: L"Windows Boot Manager", diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 4b6e8776ca..4384c9dbf0 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -26,7 +26,7 @@ static EFI_STATUS tpm1_measure_to_pcr_and_event_log( assert(description); desc_len = StrSize(description); - tcg_event = xallocate_zero_pool(OFFSETOF(TCG_PCR_EVENT, Event) + desc_len); + tcg_event = xallocate_zero_pool(offsetof(TCG_PCR_EVENT, Event) + desc_len); *tcg_event = (TCG_PCR_EVENT) { .EventSize = desc_len, .PCRIndex = pcrindex, @@ -57,9 +57,9 @@ static EFI_STATUS tpm2_measure_to_pcr_and_event_log( assert(description); desc_len = StrSize(description); - tcg_event = xallocate_zero_pool(OFFSETOF(EFI_TCG2_EVENT, Event) + desc_len); + tcg_event = xallocate_zero_pool(offsetof(EFI_TCG2_EVENT, Event) + desc_len); *tcg_event = (EFI_TCG2_EVENT) { - .Size = OFFSETOF(EFI_TCG2_EVENT, Event) + desc_len, + .Size = offsetof(EFI_TCG2_EVENT, Event) + desc_len, .Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER), .Header.HeaderVersion = EFI_TCG2_EVENT_HEADER_VERSION, .Header.PCRIndex = pcrindex, diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 66d660aad5..a108bf16ab 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -100,6 +100,7 @@ if efi_lds == '' endif efi_headers = files(''' + bcd.h console.h cpio.h devicetree.h @@ -129,6 +130,7 @@ common_sources = ''' '''.split() systemd_boot_sources = ''' + bcd.c boot.c console.c drivers.c @@ -371,6 +373,14 @@ endforeach ############################################################ +tests += [ + [['src/boot/efi/test-bcd.c'], + [], + [libzstd], + [], + 'HAVE_ZSTD'], +] + test_efi_disk_img = custom_target( 'test-efi-disk.img', input : [efi_stubs[0][0], efi_stubs[1][1]], diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index 91e7529fb1..ed3b0b8e94 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -129,7 +129,7 @@ static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) { static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) { assert(dos); assert(pe); - return dos->ExeHeader + OFFSETOF(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader; + return dos->ExeHeader + offsetof(struct PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader; } static void locate_sections( diff --git a/src/boot/efi/test-bcd.c b/src/boot/efi/test-bcd.c new file mode 100644 index 0000000000..a4d09068a5 --- /dev/null +++ b/src/boot/efi/test-bcd.c @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "compress.h" +#include "fileio.h" +#include "tests.h" +#include "utf8.h" + +/* Inlcude the implementation directly, so we can poke at some internals. */ +#include "bcd.c" + +static void load_bcd(const char *path, void **ret_bcd, size_t *ret_bcd_len) { + size_t len; + _cleanup_free_ char *fn = NULL, *compressed = NULL; + + assert_se(get_testdata_dir(path, &fn) >= 0); + assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &compressed, &len) >= 0); + assert_se(decompress_blob_zstd(compressed, len, ret_bcd, ret_bcd_len, SIZE_MAX) >= 0); +} + +static void test_get_bcd_title_one( + const char *path, + const char16_t *title_expect, + size_t title_len_expect) { + + size_t len; + _cleanup_free_ void *bcd = NULL; + + log_info("/* %s(%s) */", __func__, path); + + load_bcd(path, &bcd, &len); + + char16_t *title = get_bcd_title(bcd, len); + if (title_expect) { + assert_se(title); + assert_se(memcmp(title, title_expect, title_len_expect) == 0); + } else + assert_se(!title); +} + +TEST(get_bcd_title) { + const char16_t win10[] = { 'W', 'i', 'n', 'd', 'o', 'w', 's', ' ', '1', '0', '\0' }; + test_get_bcd_title_one("test-bcd/win10.bcd.zst", win10, sizeof(win10)); + + test_get_bcd_title_one("test-bcd/description-bad-type.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/description-empty.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/description-missing.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/description-too-small.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/displayorder-bad-name.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/displayorder-bad-size.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/displayorder-bad-type.bcd.zst", NULL, 0); + test_get_bcd_title_one("test-bcd/empty.bcd.zst", NULL, 0); +} + +TEST(base_block) { + size_t len; + BaseBlock backup; + uint8_t *bcd_base; + _cleanup_free_ BaseBlock *bcd = NULL; + + load_bcd("test-bcd/win10.bcd.zst", (void **) &bcd, &len); + backup = *bcd; + bcd_base = (uint8_t *) bcd; + + assert_se(get_bcd_title(bcd_base, len)); + + /* Try various "corruptions" of the base block. */ + + assert_se(!get_bcd_title(bcd_base, sizeof(BaseBlock) - 1)); + + bcd->sig = 0; + assert_se(!get_bcd_title(bcd_base, len)); + *bcd = backup; + + bcd->version_minor = 2; + assert_se(!get_bcd_title(bcd_base, len)); + *bcd = backup; + + bcd->version_major = 4; + assert_se(!get_bcd_title(bcd_base, len)); + *bcd = backup; + + bcd->type = 1; + assert_se(!get_bcd_title(bcd_base, len)); + *bcd = backup; + + bcd->primary_seqnum++; + assert_se(!get_bcd_title(bcd_base, len)); + *bcd = backup; +} + +TEST(bad_bcd) { + size_t len; + uint8_t *hbins; + uint32_t offset; + _cleanup_free_ void *bcd = NULL; + + /* This BCD hive has been manipulated to have bad offsets/sizes at various places. */ + load_bcd("test-bcd/corrupt.bcd.zst", &bcd, &len); + + assert_se(len >= HIVE_CELL_OFFSET); + hbins = (uint8_t *) bcd + HIVE_CELL_OFFSET; + len -= HIVE_CELL_OFFSET; + offset = ((BaseBlock *) bcd)->root_cell_offset; + + const Key *root = get_key(hbins, len, offset, "\0"); + assert_se(root); + assert_se(!get_key(hbins, sizeof(Key) - 1, offset, "\0")); + + assert_se(!get_key(hbins, len, offset, "\0BadOffset\0")); + assert_se(!get_key(hbins, len, offset, "\0BadSig\0")); + assert_se(!get_key(hbins, len, offset, "\0BadKeyNameLen\0")); + assert_se(!get_key(hbins, len, offset, "\0SubkeyBadOffset\0Dummy\0")); + assert_se(!get_key(hbins, len, offset, "\0SubkeyBadSig\0Dummy\0")); + assert_se(!get_key(hbins, len, offset, "\0SubkeyBadNEntries\0Dummy\0")); + + assert_se(!get_key_value(hbins, len, root, "Dummy")); + + const Key *kv_bad_offset = get_key(hbins, len, offset, "\0KeyValuesBadOffset\0"); + assert_se(kv_bad_offset); + assert_se(!get_key_value(hbins, len, kv_bad_offset, "Dummy")); + + const Key *kv_bad_n_key_values = get_key(hbins, len, offset, "\0KeyValuesBadNKeyValues\0"); + assert_se(kv_bad_n_key_values); + assert_se(!get_key_value(hbins, len, kv_bad_n_key_values, "Dummy")); + + const Key *kv = get_key(hbins, len, offset, "\0KeyValues\0"); + assert_se(kv); + + assert_se(!get_key_value(hbins, len, kv, "BadOffset")); + assert_se(!get_key_value(hbins, len, kv, "BadSig")); + assert_se(!get_key_value(hbins, len, kv, "BadNameLen")); + assert_se(!get_key_value(hbins, len, kv, "InlineData")); + assert_se(!get_key_value(hbins, len, kv, "BadDataOffset")); + assert_se(!get_key_value(hbins, len, kv, "BadDataSize")); +} + +TEST(argv_bcds) { + for (int i = 1; i < saved_argc; i++) { + size_t len; + _cleanup_free_ void *bcd = NULL; + + assert_se(read_full_file_full( + AT_FDCWD, + saved_argv[i], + UINT64_MAX, + SIZE_MAX, + 0, + NULL, + (char **) &bcd, + &len) >= 0); + + char16_t *title = get_bcd_title(bcd, len); + if (title) { + _cleanup_free_ char *title_utf8 = utf16_to_utf8(title, char16_strlen(title) * 2); + log_info("%s: \"%s\"", saved_argv[i], title_utf8); + } else + log_info("%s: Bad BCD", saved_argv[i]); + } +} + +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 04e390b0fa..6db4ab3969 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -505,20 +505,6 @@ EFI_STATUS log_oom(void) { 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; -} - void print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str) { assert(str); ST->ConOut->SetCursorPosition(ST->ConOut, x, y); @@ -566,7 +552,7 @@ EFI_STATUS get_file_info_harder( EFI_FILE_INFO **ret, UINTN *ret_size) { - UINTN size = OFFSETOF(EFI_FILE_INFO, FileName) + 256; + UINTN size = offsetof(EFI_FILE_INFO, FileName) + 256; _cleanup_freepool_ EFI_FILE_INFO *fi = NULL; EFI_STATUS err; @@ -610,7 +596,7 @@ EFI_STATUS readdir_harder( * the specified buffer needs to be freed by caller, after final use. */ if (!*buffer) { - sz = OFFSETOF(EFI_FILE_INFO, FileName) /* + 256 */; + sz = offsetof(EFI_FILE_INFO, FileName) /* + 256 */; *buffer = xallocate_pool(sz); *buffer_size = sz; } else @@ -649,6 +635,27 @@ UINTN strnlena(const CHAR8 *p, UINTN maxlen) { return c; } +INTN strncasecmpa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen) { + if (!a || !b) + return CMP(a, b); + + while (maxlen > 0) { + CHAR8 ca = *a, cb = *b; + if (ca >= 'A' && ca <= 'Z') + ca += 'a' - 'A'; + if (cb >= 'A' && cb <= 'Z') + cb += 'a' - 'A'; + if (!ca || ca != cb) + return ca - cb; + + a++; + b++; + maxlen--; + } + + return 0; +} + CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz) { CHAR8 *n; diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 425d25d73f..b40f05eaef 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -13,7 +13,7 @@ /* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */ #define TPM_PCR_INDEX_INITRD 4 -#define OFFSETOF(x,y) __builtin_offsetof(x,y) +#define offsetof(type, member) __builtin_offsetof(type, member) #define UINTN_MAX (~(UINTN)0) #define INTN_MAX ((INTN)(UINTN_MAX>>1)) @@ -111,13 +111,6 @@ EFI_STATUS log_oom(void); 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; -} - void print_at(UINTN x, UINTN y, UINTN attr, const CHAR16 *str); void clear_screen(UINTN attr); @@ -130,6 +123,10 @@ EFI_STATUS readdir_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **buffer, UINTN UINTN strnlena(const CHAR8 *p, UINTN maxlen); CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz); +INTN strncasecmpa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen); +static inline BOOLEAN strncaseeqa(const CHAR8 *a, const CHAR8 *b, UINTN maxlen) { + return strncasecmpa(a, b, maxlen) == 0; +} BOOLEAN is_ascii(const CHAR16 *f); diff --git a/test/meson.build b/test/meson.build index fad40b8946..7d94032d09 100644 --- a/test/meson.build +++ b/test/meson.build @@ -67,6 +67,11 @@ if install_tests '../-.mount', testsuite08_dir + '/local-fs.target.wants/-.mount') + if conf.get('HAVE_GNU_EFI') == 1 and conf.get('HAVE_ZSTD') == 1 + install_subdir('test-bcd', + exclude_files : '.gitattributes', + install_dir : testdata_dir) + endif if conf.get('ENABLE_RESOLVE') == 1 install_subdir('test-resolve', exclude_files : '.gitattributes', diff --git a/test/test-bcd/.gitattributes b/test/test-bcd/.gitattributes new file mode 100644 index 0000000000..b5f1b48c1a --- /dev/null +++ b/test/test-bcd/.gitattributes @@ -0,0 +1,2 @@ +*.bcd binary +*.bcd.zst binary diff --git a/test/test-bcd/corrupt.bcd.zst b/test/test-bcd/corrupt.bcd.zst new file mode 100644 index 0000000000..48b570161c Binary files /dev/null and b/test/test-bcd/corrupt.bcd.zst differ diff --git a/test/test-bcd/description-bad-type.bcd.zst b/test/test-bcd/description-bad-type.bcd.zst new file mode 100644 index 0000000000..fdf23ca721 Binary files /dev/null and b/test/test-bcd/description-bad-type.bcd.zst differ diff --git a/test/test-bcd/description-empty.bcd.zst b/test/test-bcd/description-empty.bcd.zst new file mode 100644 index 0000000000..c1c801d271 Binary files /dev/null and b/test/test-bcd/description-empty.bcd.zst differ diff --git a/test/test-bcd/description-missing.bcd.zst b/test/test-bcd/description-missing.bcd.zst new file mode 100644 index 0000000000..a34ad8a4c1 Binary files /dev/null and b/test/test-bcd/description-missing.bcd.zst differ diff --git a/test/test-bcd/description-too-small.bcd.zst b/test/test-bcd/description-too-small.bcd.zst new file mode 100644 index 0000000000..76e739369e Binary files /dev/null and b/test/test-bcd/description-too-small.bcd.zst differ diff --git a/test/test-bcd/displayorder-bad-name.bcd.zst b/test/test-bcd/displayorder-bad-name.bcd.zst new file mode 100644 index 0000000000..157f54e355 Binary files /dev/null and b/test/test-bcd/displayorder-bad-name.bcd.zst differ diff --git a/test/test-bcd/displayorder-bad-size.bcd.zst b/test/test-bcd/displayorder-bad-size.bcd.zst new file mode 100644 index 0000000000..ee89b7709b Binary files /dev/null and b/test/test-bcd/displayorder-bad-size.bcd.zst differ diff --git a/test/test-bcd/displayorder-bad-type.bcd.zst b/test/test-bcd/displayorder-bad-type.bcd.zst new file mode 100644 index 0000000000..cc2a8879e7 Binary files /dev/null and b/test/test-bcd/displayorder-bad-type.bcd.zst differ diff --git a/test/test-bcd/empty.bcd.zst b/test/test-bcd/empty.bcd.zst new file mode 100644 index 0000000000..5b3e938493 Binary files /dev/null and b/test/test-bcd/empty.bcd.zst differ diff --git a/test/test-bcd/win10.bcd.zst b/test/test-bcd/win10.bcd.zst new file mode 100644 index 0000000000..c922eb6042 Binary files /dev/null and b/test/test-bcd/win10.bcd.zst differ