:: commit 095cf5ef7980504e49338514dfd9ada499e4084d

Mintsuki <mintsuki@protonmail.com> — 2026-02-07 05:42

parents: 84fad36b41

lib/elf: Validate p_offset + p_filesz against file size in all ELF loaders

diff --git a/common/lib/elf.c b/common/lib/elf.c
index fff63d91..58eb12a3 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -752,7 +752,7 @@ static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct mem_range **_r
     *_ranges = ranges;
 }
 
-bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
+bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
     struct elf64_hdr *hdr = (void *)elf;
 
     elf64_validate(hdr);
@@ -914,11 +914,14 @@ again:
             panic(true, "elf: p_filesz > p_memsz");
         }
 
-        // Validate p_offset + p_filesz doesn't overflow
+        // Validate p_offset + p_filesz doesn't overflow or exceed file size
         uint64_t offset_end;
         if (__builtin_add_overflow(phdr->p_offset, phdr->p_filesz, &offset_end)) {
             panic(true, "elf: p_offset + p_filesz overflow");
         }
+        if (offset_end > file_size) {
+            panic(true, "elf: p_offset + p_filesz exceeds file size");
+        }
 
         uint64_t load_addr = *physical_base + (phdr->p_vaddr - *virtual_base);
 
@@ -965,7 +968,7 @@ again:
     return true;
 }
 
-bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+bool elf32_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
                           struct elsewhere_range **ranges) {
     struct elf32_hdr *hdr = (void *)elf;
 
@@ -1020,6 +1023,9 @@ bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
         if (phdr->p_filesz > phdr->p_memsz) {
             panic(true, "elf: p_filesz > p_memsz");
         }
+        if ((uint64_t)phdr->p_offset + phdr->p_filesz > file_size) {
+            panic(true, "elf: p_offset + p_filesz exceeds file size");
+        }
 
         memcpy(elsewhere + (phdr->p_paddr - min_paddr), elf + phdr->p_offset, phdr->p_filesz);
 
@@ -1035,7 +1041,7 @@ bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
     return true;
 }
 
-bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+bool elf64_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
                           struct elsewhere_range **ranges) {
     struct elf64_hdr *hdr = (void *)elf;
 
@@ -1093,6 +1099,11 @@ bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
         if (phdr->p_filesz > phdr->p_memsz) {
             panic(true, "elf: p_filesz > p_memsz");
         }
+        uint64_t offset_end;
+        if (__builtin_add_overflow(phdr->p_offset, phdr->p_filesz, &offset_end)
+            || offset_end > file_size) {
+            panic(true, "elf: p_offset + p_filesz exceeds file size");
+        }
 
         memcpy(elsewhere + (phdr->p_paddr - min_paddr), elf + phdr->p_offset, phdr->p_filesz);
 
diff --git a/common/lib/elf.h b/common/lib/elf.h
index 7a1d66a5..ef980228 100644
--- a/common/lib/elf.h
+++ b/common/lib/elf.h
@@ -25,11 +25,11 @@ struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf);
 struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf);
 
 bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
-bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
+bool elf64_load(uint8_t *elf, size_t file_size, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
 
-bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+bool elf32_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
                           struct elsewhere_range **ranges);
-bool elf64_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
+bool elf64_load_elsewhere(uint8_t *elf, size_t file_size, uint64_t *entry_point,
                           struct elsewhere_range **ranges);
 
 struct elf64_hdr {
diff --git a/common/protos/limine.c b/common/protos/limine.c
index 0dd44d3f..53f46e13 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -478,7 +478,7 @@ noreturn void limine_load(char *config, char *cmdline) {
     enum executable_format kernel_format = detect_kernel_format(kernel, kernel_file->size);
     switch (kernel_format) {
         case EXECUTABLE_FORMAT_ELF:
-            if (!elf64_load(kernel, &entry_point, &slide,
+            if (!elf64_load(kernel, kernel_file->size, &entry_point, &slide,
                             MEMMAP_KERNEL_AND_MODULES, kaslr,
                             &ranges, &ranges_count,
                             &physical_base, &virtual_base, NULL,
diff --git a/common/protos/multiboot1.c b/common/protos/multiboot1.c
index b80d92ce..874b40a2 100644
--- a/common/protos/multiboot1.c
+++ b/common/protos/multiboot1.c
@@ -156,14 +156,14 @@ noreturn void multiboot1_load(char *config, char *cmdline) {
 
         switch (bits) {
             case 32:
-                if (!elf32_load_elsewhere(kernel, &entry_point, &ranges))
+                if (!elf32_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges))
                     panic(true, "multiboot1: ELF32 load failure");
 
                 section_hdr_info = elf32_section_hdr_info(kernel);
                 section_hdr_info_valid = true;
                 break;
             case 64: {
-                if (!elf64_load_elsewhere(kernel, &entry_point, &ranges))
+                if (!elf64_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges))
                     panic(true, "multiboot1: ELF64 load failure");
 
                 section_hdr_info = elf64_section_hdr_info(kernel);
diff --git a/common/protos/multiboot2.c b/common/protos/multiboot2.c
index ed7930ac..6dbb59d0 100644
--- a/common/protos/multiboot2.c
+++ b/common/protos/multiboot2.c
@@ -328,14 +328,14 @@ noreturn void multiboot2_load(char *config, char* cmdline) {
 
         switch (bits) {
             case 32:
-                if (!elf32_load_elsewhere(kernel, &e, &ranges))
+                if (!elf32_load_elsewhere(kernel, kernel_file_size, &e, &ranges))
                     panic(true, "multiboot2: ELF32 load failure");
 
                 section_hdr_info = elf32_section_hdr_info(kernel);
                 section_hdr_info_valid = true;
                 break;
             case 64: {
-                if (!elf64_load_elsewhere(kernel, &e, &ranges))
+                if (!elf64_load_elsewhere(kernel, kernel_file_size, &e, &ranges))
                     panic(true, "multiboot2: ELF64 load failure");
 
                 section_hdr_info = elf64_section_hdr_info(kernel);
tab: 248 wrap: offon