summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2024-06-28 19:54:46 +0200
committerLennart Poettering <lennart@poettering.net>2024-09-10 06:49:08 +0200
commitf4e081051d950a09ce9331ba55eaf604dac72652 (patch)
tree39ca6f324f4ec9d3aefec0ac1071869440322681
parentefi: add free_and_xstrdup16() helper modelled after free_and_strdup() in user... (diff)
downloadsystemd-f4e081051d950a09ce9331ba55eaf604dac72652.tar.xz
systemd-f4e081051d950a09ce9331ba55eaf604dac72652.zip
efi: teach PE parsing support for ".profile" sections
This adds helpers for: 1. Returning the PE section table of open PE files or memory 2. Scanning PE section tables for the sections that belong to a specific profile
-rw-r--r--src/boot/efi/pe.c216
-rw-r--r--src/boot/efi/pe.h32
2 files changed, 199 insertions, 49 deletions
diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c
index 5817806d72..7eb8c4fa20 100644
--- a/src/boot/efi/pe.c
+++ b/src/boot/efi/pe.c
@@ -106,19 +106,6 @@ typedef struct PeFileHeader {
PeOptionalHeader OptionalHeader;
} _packed_ PeFileHeader;
-typedef struct PeSectionHeader {
- uint8_t Name[8];
- uint32_t VirtualSize;
- uint32_t VirtualAddress;
- uint32_t SizeOfRawData;
- uint32_t PointerToRawData;
- uint32_t PointerToRelocations;
- uint32_t PointerToLinenumbers;
- uint16_t NumberOfRelocations;
- uint16_t NumberOfLinenumbers;
- uint32_t Characteristics;
-} _packed_ PeSectionHeader;
-
#define SECTION_TABLE_BYTES_MAX (16U * 1024U * 1024U)
static bool verify_dos(const DosFileHeader *dos) {
@@ -188,7 +175,7 @@ static void pe_locate_sections(
/* 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. */
+ * checking for overflows. */
for (size_t i = 0; section_names[i]; i++)
FOREACH_ARRAY(j, section_table, n_section_table) {
@@ -313,59 +300,69 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address, size_t
return EFI_SUCCESS;
}
-EFI_STATUS pe_memory_locate_sections(
+EFI_STATUS pe_section_table_from_base(
const void *base,
- const char *const section_names[],
- PeSectionVector sections[]) {
-
- const DosFileHeader *dos;
- const PeFileHeader *pe;
- size_t offset;
+ const PeSectionHeader **ret_section_table,
+ size_t *ret_n_section_table) {
assert(base);
- assert(section_names);
- assert(sections);
+ assert(ret_section_table);
+ assert(ret_n_section_table);
- dos = (const DosFileHeader *) base;
+ const DosFileHeader *dos = (const DosFileHeader*) base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
- pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
+ const PeFileHeader *pe = (const PeFileHeader*) ((const uint8_t*) base + dos->ExeHeader);
if (!verify_pe(dos, pe, /* allow_compatibility= */ false))
return EFI_LOAD_ERROR;
- offset = section_table_offset(dos, pe);
- pe_locate_sections(
- (const PeSectionHeader *) ((const uint8_t *) base + offset),
- pe->FileHeader.NumberOfSections,
- section_names,
- PTR_TO_SIZE(base),
- sections);
+ *ret_section_table = (const PeSectionHeader*) ((const uint8_t*) base + section_table_offset(dos, pe));
+ *ret_n_section_table = pe->FileHeader.NumberOfSections;
return EFI_SUCCESS;
}
-EFI_STATUS pe_file_locate_sections(
- EFI_FILE *dir,
- const char16_t *path,
+EFI_STATUS pe_memory_locate_sections(
+ const void *base,
const char *const section_names[],
PeSectionVector sections[]) {
- _cleanup_free_ PeSectionHeader *section_table = NULL;
- _cleanup_(file_closep) EFI_FILE *handle = NULL;
- DosFileHeader dos;
- PeFileHeader pe;
- size_t len, section_table_len;
+
EFI_STATUS err;
- assert(dir);
- assert(path);
+ assert(base);
assert(section_names);
assert(sections);
- err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
+ const PeSectionHeader *section_table;
+ size_t n_section_table;
+ err = pe_section_table_from_base(base, &section_table, &n_section_table);
if (err != EFI_SUCCESS)
return err;
+ pe_locate_sections(
+ section_table,
+ n_section_table,
+ section_names,
+ PTR_TO_SIZE(base),
+ sections);
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS pe_section_table_from_file(
+ EFI_FILE *handle,
+ PeSectionHeader **ret_section_table,
+ size_t *ret_n_section_table) {
+
+ EFI_STATUS err;
+ size_t len;
+
+ assert(handle);
+ assert(ret_section_table);
+ assert(ret_n_section_table);
+
+ DosFileHeader dos;
len = sizeof(dos);
err = handle->Read(handle, &len, &dos);
if (err != EFI_SUCCESS)
@@ -377,6 +374,7 @@ EFI_STATUS pe_file_locate_sections(
if (err != EFI_SUCCESS)
return err;
+ PeFileHeader pe;
len = sizeof(pe);
err = handle->Read(handle, &len, &pe);
if (err != EFI_SUCCESS)
@@ -388,10 +386,11 @@ EFI_STATUS pe_file_locate_sections(
if ((size_t) pe.FileHeader.NumberOfSections > SIZE_MAX / sizeof(PeSectionHeader))
return EFI_OUT_OF_RESOURCES;
REENABLE_WARNING;
- section_table_len = (size_t) pe.FileHeader.NumberOfSections * sizeof(PeSectionHeader);
- if (section_table_len > SECTION_TABLE_BYTES_MAX)
+ size_t n_section_table = (size_t) pe.FileHeader.NumberOfSections;
+ if (n_section_table * sizeof(PeSectionHeader) > SECTION_TABLE_BYTES_MAX)
return EFI_OUT_OF_RESOURCES;
- section_table = xmalloc(section_table_len);
+
+ _cleanup_free_ PeSectionHeader *section_table = xnew(PeSectionHeader, n_section_table);
if (!section_table)
return EFI_OUT_OF_RESOURCES;
@@ -399,19 +398,138 @@ EFI_STATUS pe_file_locate_sections(
if (err != EFI_SUCCESS)
return err;
- len = section_table_len;
+ len = n_section_table * sizeof(PeSectionHeader);
err = handle->Read(handle, &len, section_table);
if (err != EFI_SUCCESS)
return err;
- if (len != section_table_len)
+ if (len != n_section_table * sizeof(PeSectionHeader))
return EFI_LOAD_ERROR;
+ *ret_section_table = TAKE_PTR(section_table);
+ *ret_n_section_table = n_section_table;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS pe_file_locate_sections(
+ EFI_FILE *dir,
+ const char16_t *path,
+ const char* const section_names[],
+ PeSectionVector sections[]) {
+
+ _cleanup_free_ PeSectionHeader *section_table = NULL;
+ _cleanup_(file_closep) EFI_FILE *handle = NULL;
+ size_t n_section_table;
+ EFI_STATUS err;
+
+ assert(dir);
+ assert(path);
+ assert(section_names);
+ assert(sections);
+
+ err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ err = pe_section_table_from_file(handle, &section_table, &n_section_table);
+ if (err != EFI_SUCCESS)
+ return err;
+
pe_locate_sections(
section_table,
- pe.FileHeader.NumberOfSections,
+ n_section_table,
section_names,
/* validate_base= */ 0, /* don't validate base */
sections);
return EFI_SUCCESS;
}
+
+static const PeSectionHeader* pe_section_table_find_profile_start(
+ const PeSectionHeader *section_table,
+ size_t n_section_table,
+ unsigned profile) {
+
+ assert(section_table || n_section_table == 0);
+
+ if (profile == UINT_MAX) /* base profile? that starts at the beginning */
+ return section_table;
+
+ unsigned current_profile = UINT_MAX;
+ FOREACH_ARRAY(p, section_table, n_section_table) {
+
+ if (!pe_section_name_equal((const char*) p->Name, ".profile"))
+ continue;
+
+ if (current_profile == UINT_MAX)
+ current_profile = 0;
+ else
+ current_profile++;
+
+ if (current_profile == profile) /* Found our profile! */
+ return p;
+ }
+
+ /* We reached the end of the table? Then this section does not exist */
+ return NULL;
+}
+
+static size_t pe_section_table_find_profile_length(
+ const PeSectionHeader *section_table,
+ size_t n_section_table,
+ const PeSectionHeader *start,
+ unsigned profile) {
+
+ assert(section_table);
+ assert(n_section_table > 0);
+ assert(start >= section_table);
+ assert(start < section_table + n_section_table);
+
+ /* Look for the next .profile (or the end of the table), this is where the the sections for this
+ * profile end. The base profile does not start with a .profile, the others do, hence conditionally
+ * skip over the first entry. */
+ const PeSectionHeader *e;
+ if (profile == UINT_MAX) /* Base profile */
+ e = start;
+ else {
+ assert(pe_section_name_equal((const char *) start->Name, ".profile"));
+ e = start + 1;
+ }
+
+ for (; e < section_table + n_section_table; e++)
+ if (pe_section_name_equal((const char*) e->Name, ".profile"))
+ return e - start;
+
+ return (section_table + n_section_table) - start;
+}
+
+EFI_STATUS pe_locate_profile_sections(
+ const PeSectionHeader section_table[],
+ size_t n_section_table,
+ const char* const section_names[],
+ unsigned profile,
+ size_t validate_base,
+ PeSectionVector sections[]) {
+
+ assert(section_table || n_section_table == 0);
+ assert(section_names);
+ assert(sections);
+
+ /* Now scan through the section table until we skipped over the right number of .profile sections */
+ const PeSectionHeader *p = pe_section_table_find_profile_start(section_table, n_section_table, profile);
+ if (!p)
+ return EFI_NOT_FOUND;
+
+ /* Look for the next .profile (or the end of the table), this is where the the sections for this
+ * profile end. */
+ size_t n = pe_section_table_find_profile_length(section_table, n_section_table, p, profile);
+
+ /* And now parse everything between the start and end of our profile */
+ pe_locate_sections(
+ p,
+ n,
+ section_names,
+ validate_base,
+ sections);
+
+ return EFI_SUCCESS;
+}
diff --git a/src/boot/efi/pe.h b/src/boot/efi/pe.h
index 863f870b9f..31efbebbeb 100644
--- a/src/boot/efi/pe.h
+++ b/src/boot/efi/pe.h
@@ -3,6 +3,20 @@
#include "efi.h"
+/* This is the actual PE format of the section header*/
+typedef struct PeSectionHeader {
+ uint8_t Name[8];
+ uint32_t VirtualSize;
+ uint32_t VirtualAddress;
+ uint32_t SizeOfRawData;
+ uint32_t PointerToRawData;
+ uint32_t PointerToRelocations;
+ uint32_t PointerToLinenumbers;
+ uint16_t NumberOfRelocations;
+ uint16_t NumberOfLinenumbers;
+ uint32_t Characteristics;
+} _packed_ PeSectionHeader;
+
/* This is a subset of the full PE section header structure, with validated values, and without
* the noise. */
typedef struct PeSectionVector {
@@ -15,6 +29,24 @@ static inline bool PE_SECTION_VECTOR_IS_SET(const PeSectionVector *v) {
return v && v->size != 0;
}
+EFI_STATUS pe_section_table_from_base(
+ const void *base,
+ const PeSectionHeader **ret_section_table,
+ size_t *ret_n_section_table);
+
+EFI_STATUS pe_section_table_from_file(
+ EFI_FILE *handle,
+ PeSectionHeader **ret_section_table,
+ size_t *ret_n_section_table);
+
+EFI_STATUS pe_locate_profile_sections(
+ const PeSectionHeader section_table[],
+ size_t n_section_table,
+ const char* const section_names[],
+ unsigned profile,
+ size_t validate_base,
+ PeSectionVector sections[]);
+
EFI_STATUS pe_memory_locate_sections(
const void *base,
const char *const section_names[],