mirror of
https://github.com/systemd/systemd.git
synced 2025-03-31 14:50:15 +03:00
Merge pull request #21614 from medhefgo/boot-bcd
boot: Add BCD store parser
This commit is contained in:
commit
6d7bc744ce
13
meson.build
13
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',
|
||||
|
321
src/boot/efi/bcd.c
Normal file
321
src/boot/efi/bcd.c
Normal file
@ -0,0 +1,321 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#ifdef SD_BOOT
|
||||
# include <efi.h>
|
||||
# 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 <stddef.h>
|
||||
# include <stdint.h>
|
||||
# include <uchar.h>
|
||||
# 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;
|
||||
}
|
6
src/boot/efi/bcd.h
Normal file
6
src/boot/efi/bcd.h
Normal file
@ -0,0 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
|
||||
CHAR16 *get_bcd_title(UINT8 *bcd, UINTN bcd_len);
|
@ -4,6 +4,7 @@
|
||||
#include <efigpt.h>
|
||||
#include <efilib.h>
|
||||
|
||||
#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",
|
||||
|
@ -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,
|
||||
|
@ -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]],
|
||||
|
@ -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(
|
||||
|
162
src/boot/efi/test-bcd.c
Normal file
162
src/boot/efi/test-bcd.c
Normal file
@ -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);
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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',
|
||||
|
2
test/test-bcd/.gitattributes
vendored
Normal file
2
test/test-bcd/.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.bcd binary
|
||||
*.bcd.zst binary
|
BIN
test/test-bcd/corrupt.bcd.zst
Normal file
BIN
test/test-bcd/corrupt.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/description-bad-type.bcd.zst
Normal file
BIN
test/test-bcd/description-bad-type.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/description-empty.bcd.zst
Normal file
BIN
test/test-bcd/description-empty.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/description-missing.bcd.zst
Normal file
BIN
test/test-bcd/description-missing.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/description-too-small.bcd.zst
Normal file
BIN
test/test-bcd/description-too-small.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/displayorder-bad-name.bcd.zst
Normal file
BIN
test/test-bcd/displayorder-bad-name.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/displayorder-bad-size.bcd.zst
Normal file
BIN
test/test-bcd/displayorder-bad-size.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/displayorder-bad-type.bcd.zst
Normal file
BIN
test/test-bcd/displayorder-bad-type.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/empty.bcd.zst
Normal file
BIN
test/test-bcd/empty.bcd.zst
Normal file
Binary file not shown.
BIN
test/test-bcd/win10.bcd.zst
Normal file
BIN
test/test-bcd/win10.bcd.zst
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user