]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
efi: teach PE parsing support for ".profile" sections
authorLennart Poettering <lennart@poettering.net>
Fri, 28 Jun 2024 17:54:46 +0000 (19:54 +0200)
committerLennart Poettering <lennart@poettering.net>
Tue, 10 Sep 2024 04:49:08 +0000 (06:49 +0200)
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

src/boot/efi/pe.c
src/boot/efi/pe.h

index 5817806d72badc15aef97e57031b08903f9c8818..7eb8c4fa20219436dcd83294aacd1ac94a00d3c0 100644 (file)
@@ -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;
+}
index 863f870b9f1a242bfcad514fe04b8d565398ab37..31efbebbeb6277073d177dc55995d02ba469272f 100644 (file)
@@ -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[],