}
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;
_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,
*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)
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;
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);
} _packed_ PeOptionalHeader;
typedef struct PeFileHeader {
- uint8_t Magic[4];
+ uint8_t Magic[4];
CoffFileHeader FileHeader;
PeOptionalHeader OptionalHeader;
} _packed_ PeFileHeader;
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;
+
+ /* 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;
- offsets[j] = in_memory ? sect->VirtualAddress : sect->PointerToRawData;
- sizes[j] = sect->VirtualSize;
+ /* 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 {
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);
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))
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;
}
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;
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)
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;
}
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;
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,
}
/* 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,
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;
(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");
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. */
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 */
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);
}
(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
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);
}
&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) {
* 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,
* 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,
&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) {
return err;
}
-DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /*wait_for_debugger=*/false);
+DEFINE_EFI_MAIN_FUNCTION(run, "systemd-stub", /* wait_for_debugger= */ false);