]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pe: tighten validity checks of DOS and PE headers
authorLennart Poettering <lennart@poettering.net>
Mon, 24 Jun 2024 15:40:32 +0000 (17:40 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 26 Jun 2024 15:09:44 +0000 (17:09 +0200)
Let's make sure we validate that the DOS/PE header offsets are actually
reasonable, and do not cause overflows when added to the base addresses.

(This shouldn're really be a problem URL, since the DOS header offset is
16bit only, but let's be extra careful with this)

src/boot/efi/pe.c

index 8c5e5f732d5b82bc6c4208e379a95f5a30b9669a..34b01561f07de20248e5c65b3f1f17c127285063 100644 (file)
@@ -123,22 +123,35 @@ typedef struct PeSectionHeader {
 
 static bool verify_dos(const DosFileHeader *dos) {
         assert(dos);
-        return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
+
+        DISABLE_WARNING_TYPE_LIMITS;
+        return memcmp(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0 &&
+                dos->ExeHeader >= sizeof(DosFileHeader) &&
+                (size_t) dos->ExeHeader <= SIZE_MAX - sizeof(PeFileHeader);
+        REENABLE_WARNING;
 }
 
-static bool verify_pe(const PeFileHeader *pe, bool allow_compatibility) {
+static bool verify_pe(
+                const DosFileHeader *dos,
+                const PeFileHeader *pe,
+                bool allow_compatibility) {
+
+        assert(dos);
         assert(pe);
+
         return memcmp(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
-               (pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
-                (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
-               pe->FileHeader.NumberOfSections > 0 &&
-               pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
-               IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC);
+                (pe->FileHeader.Machine == TARGET_MACHINE_TYPE ||
+                 (allow_compatibility && pe->FileHeader.Machine == TARGET_MACHINE_TYPE_COMPATIBILITY)) &&
+                pe->FileHeader.NumberOfSections > 0 &&
+                pe->FileHeader.NumberOfSections <= MAX_SECTIONS &&
+                IN_SET(pe->OptionalHeader.Magic, OPTHDR32_MAGIC, OPTHDR64_MAGIC) &&
+                pe->FileHeader.SizeOfOptionalHeader < SIZE_MAX - (dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader));
 }
 
 static size_t section_table_offset(const DosFileHeader *dos, const PeFileHeader *pe) {
         assert(dos);
         assert(pe);
+
         return dos->ExeHeader + offsetof(PeFileHeader, OptionalHeader) + pe->FileHeader.SizeOfOptionalHeader;
 }
 
@@ -220,7 +233,7 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
                 return EFI_LOAD_ERROR;
 
         const PeFileHeader *pe = (const PeFileHeader *) ((const uint8_t *) base + dos->ExeHeader);
-        if (!verify_pe(pe, /* allow_compatibility= */ true))
+        if (!verify_pe(dos, pe, /* allow_compatibility= */ true))
                 return EFI_LOAD_ERROR;
 
         /* Support for LINUX_INITRD_MEDIA_GUID was added in kernel stub 1.0. */
@@ -255,8 +268,8 @@ EFI_STATUS pe_memory_locate_sections(const void *base, const char * const sectio
         if (!verify_dos(dos))
                 return EFI_LOAD_ERROR;
 
-        pe = (const PeFileHeader *) ((uint8_t *) base + dos->ExeHeader);
-        if (!verify_pe(pe, /* allow_compatibility= */ false))
+        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);
@@ -308,7 +321,7 @@ EFI_STATUS pe_file_locate_sections(
         err = handle->Read(handle, &len, &pe);
         if (err != EFI_SUCCESS)
                 return err;
-        if (len != sizeof(pe) || !verify_pe(&pe, /* allow_compatibility= */ false))
+        if (len != sizeof(pe) || !verify_pe(&dos, &pe, /* allow_compatibility= */ false))
                 return EFI_LOAD_ERROR;
 
         DISABLE_WARNING_TYPE_LIMITS;