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);
