diff options
author | Lennart Poettering <lennart@poettering.net> | 2024-06-28 19:54:46 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-09-10 06:49:08 +0200 |
commit | f4e081051d950a09ce9331ba55eaf604dac72652 (patch) | |
tree | 39ca6f324f4ec9d3aefec0ac1071869440322681 | |
parent | efi: add free_and_xstrdup16() helper modelled after free_and_strdup() in user... (diff) | |
download | systemd-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.c | 216 | ||||
-rw-r--r-- | src/boot/efi/pe.h | 32 |
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, §ion_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, §ion_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[], |