:: commit 30b750a2ad1c95914d4007d7c80df708ebeeb9a3

mintsuki <mintsuki@protonmail.com> — 2021-03-26 14:47

parents: 621a004bf2

elf: Always do ASLR when loading relocatable ELFs and handle unavailable memory ranges instead of crashing

diff --git a/stage23/fs/file.s2.c b/stage23/fs/file.s2.c
index aae2c727..bd7a9628 100644
--- a/stage23/fs/file.s2.c
+++ b/stage23/fs/file.s2.c
@@ -113,7 +113,7 @@ int fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) {
 
 void *freadall(struct file_handle *fd, uint32_t type) {
     if (fd->is_memfile) {
-        memmap_alloc_range((uint64_t)(size_t)fd->fd, fd->size, type, false, true);
+        memmap_alloc_range((uint64_t)(size_t)fd->fd, fd->size, type, false, true, false);
         return fd->fd;
     } else {
         void *ret = ext_mem_alloc_aligned_type(fd->size, 4096, type);
diff --git a/stage23/lib/elf.c b/stage23/lib/elf.c
index b8b60d7d..4a706e73 100644
--- a/stage23/lib/elf.c
+++ b/stage23/lib/elf.c
@@ -4,9 +4,12 @@
 #include <lib/libc.h>
 #include <lib/elf.h>
 #include <lib/print.h>
+#include <lib/rand.h>
 #include <mm/pmm.h>
 #include <fs/file.h>
 
+#define KASLR_SLIDE_BITMASK ((uintptr_t)0x3ffff000)
+
 #define PT_LOAD     0x00000001
 #define PT_INTERP   0x00000003
 #define PT_PHDR     0x00000006
@@ -133,6 +136,27 @@ int elf_bits(struct file_handle *fd) {
     }
 }
 
+static bool elf64_is_relocatable(struct file_handle *fd, struct elf64_hdr *hdr) {
+    // Find RELA sections
+    for (uint16_t i = 0; i < hdr->sh_num; i++) {
+        struct elf64_shdr section;
+        fread(fd, &section, hdr->shoff + i * sizeof(struct elf64_shdr),
+                    sizeof(struct elf64_shdr));
+
+        if (section.sh_type != SHT_RELA)
+            continue;
+
+        if (section.sh_entsize != sizeof(struct elf64_rela)) {
+            print("elf: Unknown sh_entsize for RELA section!\n");
+            continue;
+        }
+
+        return true;
+    }
+
+    return false;
+}
+
 static int elf64_apply_relocations(struct file_handle *fd, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
     // Find RELA sections
     for (uint16_t i = 0; i < hdr->sh_num; i++) {
@@ -268,7 +292,7 @@ int elf32_load_section(struct file_handle *fd, void *buffer, const char *name, s
     return 2;
 }
 
-int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide, uint32_t alloc_type) {
+int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type) {
     struct elf64_hdr hdr;
     fread(fd, &hdr, 0, sizeof(struct elf64_hdr));
 
@@ -287,8 +311,20 @@ int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uin
         return -1;
     }
 
-    *top = 0;
+    uint64_t slide = 0;
+    bool simulation = true;
+    size_t try_count = 0;
+    size_t max_simulated_tries = 250;
+
+    if (!elf64_is_relocatable(fd, &hdr)) {
+        simulation = false;
+        goto final;
+    }
+
+again:
+    slide = rand64() & KASLR_SLIDE_BITMASK;
 
+final:
     for (uint16_t i = 0; i < hdr.ph_num; i++) {
         struct elf64_phdr phdr;
         fread(fd, &phdr, hdr.phoff + i * sizeof(struct elf64_phdr),
@@ -304,12 +340,11 @@ int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uin
 
         load_vaddr += slide;
 
-        uint64_t this_top = load_vaddr + phdr.p_memsz;
-
-        if (this_top > *top)
-            *top = this_top;
-
-        memmap_alloc_range((size_t)load_vaddr, (size_t)phdr.p_memsz, alloc_type, true, true);
+        if (!memmap_alloc_range((size_t)load_vaddr, (size_t)phdr.p_memsz, alloc_type, true, false, simulation)) {
+            if (++try_count == max_simulated_tries || simulation == false)
+                return -1;
+            goto again;
+        }
 
         fread(fd, (void *)(uintptr_t)load_vaddr, phdr.p_offset, phdr.p_filesz);
 
@@ -324,12 +359,18 @@ int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uin
             return -1;
     }
 
+    if (simulation) {
+        simulation = false;
+        goto final;
+    }
+
     *entry_point = hdr.entry + slide;
+    *_slide = slide;
 
     return 0;
 }
 
-int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t *top, uint32_t alloc_type) {
+int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t alloc_type) {
     struct elf32_hdr hdr;
     fread(fd, &hdr, 0, sizeof(struct elf32_hdr));
 
@@ -348,8 +389,6 @@ int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t *top, uin
         return -1;
     }
 
-    *top = 0;
-
     for (uint16_t i = 0; i < hdr.ph_num; i++) {
         struct elf32_phdr phdr;
         fread(fd, &phdr, hdr.phoff + i * sizeof(struct elf32_phdr),
@@ -358,11 +397,7 @@ int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t *top, uin
         if (phdr.p_type != PT_LOAD)
             continue;
 
-        uint32_t this_top = phdr.p_vaddr + phdr.p_memsz;
-        if (this_top > *top)
-            *top = this_top;
-
-        memmap_alloc_range((size_t)phdr.p_paddr, (size_t)phdr.p_memsz, alloc_type, true, true);
+        memmap_alloc_range((size_t)phdr.p_paddr, (size_t)phdr.p_memsz, alloc_type, true, true, false);
 
         fread(fd, (void *)(uintptr_t)phdr.p_paddr, phdr.p_offset, phdr.p_filesz);
 
diff --git a/stage23/lib/elf.h b/stage23/lib/elf.h
index c3467e6b..331ca063 100644
--- a/stage23/lib/elf.h
+++ b/stage23/lib/elf.h
@@ -8,10 +8,10 @@
 
 int elf_bits(struct file_handle *fd);
 
-int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide, uint32_t alloc_type);
+int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *slide, uint32_t alloc_type);
 int elf64_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit, uint64_t slide);
 
-int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t *top, uint32_t alloc_type);
+int elf32_load(struct file_handle *fd, uint32_t *entry_point, uint32_t alloc_type);
 int elf32_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit);
 
 #endif
diff --git a/stage23/mm/pmm.h b/stage23/mm/pmm.h
index 74c15172..5d3d057c 100644
--- a/stage23/mm/pmm.h
+++ b/stage23/mm/pmm.h
@@ -25,7 +25,7 @@ extern size_t memmap_entries;
 void init_memmap(void);
 struct e820_entry_t *get_memmap(size_t *entries);
 void print_memmap(struct e820_entry_t *mm, size_t size);
-bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool panic);
+bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool panic, bool simulation);
 
 void *ext_mem_alloc(size_t count);
 void *ext_mem_alloc_type(size_t count, uint32_t type);
diff --git a/stage23/mm/pmm.s2.c b/stage23/mm/pmm.s2.c
index 4577724c..d39bbaea 100644
--- a/stage23/mm/pmm.s2.c
+++ b/stage23/mm/pmm.s2.c
@@ -342,7 +342,7 @@ void init_memmap(void) {
 
     memmap_alloc_range(bump_allocator_base,
                        bump_allocator_limit - bump_allocator_base,
-                       MEMMAP_REMOVE_RANGE, true, true);
+                       MEMMAP_REMOVE_RANGE, true, true, false);
 
     print("pmm: Conventional mem allocator base:  %X\n", bump_allocator_base);
     print("pmm: Conventional mem allocator limit: %X\n", bump_allocator_limit);
@@ -399,7 +399,7 @@ void *ext_mem_alloc_aligned_type(size_t count, size_t alignment, uint32_t type)
 
         // We now reserve the range we need.
         int64_t aligned_length = entry_top - alloc_base;
-        memmap_alloc_range((uint64_t)alloc_base, (uint64_t)aligned_length, type, true, true);
+        memmap_alloc_range((uint64_t)alloc_base, (uint64_t)aligned_length, type, true, true, false);
 
         void *ret = (void *)(size_t)alloc_base;
 
@@ -414,7 +414,7 @@ void *ext_mem_alloc_aligned_type(size_t count, size_t alignment, uint32_t type)
     panic("High memory allocator: Out of memory");
 }
 
-bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool do_panic) {
+bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free_only, bool do_panic, bool simulation) {
     if (length == 0)
         return true;
 
@@ -443,6 +443,9 @@ bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free
         if (type == MEMMAP_REMOVE_RANGE &&
             base == entry_base && top == entry_top) {
 
+            if (simulation)
+                return true;
+
             // Eradicate from memmap
             for (size_t j = i; j < memmap_entries - 1; j++) {
                 memmap[j] = memmap[j+1];
@@ -454,6 +457,10 @@ bool memmap_alloc_range(uint64_t base, uint64_t length, uint32_t type, bool free
 
         if (base >= entry_base && base <  entry_top &&
             top  >= entry_base && top  <= entry_top) {
+
+            if (simulation)
+                return true;
+
             struct e820_entry_t *target;
 
             memmap[i].length -= entry_top - base;
diff --git a/stage23/protos/linux.c b/stage23/protos/linux.c
index c2a07640..513197e1 100644
--- a/stage23/protos/linux.c
+++ b/stage23/protos/linux.c
@@ -417,7 +417,7 @@ void linux_load(char *config, char *cmdline) {
     for (;;) {
         if (memmap_alloc_range(kernel_load_addr,
                 kernel->size - real_mode_code_size,
-                MEMMAP_BOOTLOADER_RECLAIMABLE, true, false))
+                MEMMAP_BOOTLOADER_RECLAIMABLE, true, false, false))
             break;
 
         kernel_load_addr += 0x100000;
@@ -451,7 +451,7 @@ void linux_load(char *config, char *cmdline) {
 
     for (;;) {
         if (memmap_alloc_range(modules_mem_base, size_of_all_modules,
-                               MEMMAP_BOOTLOADER_RECLAIMABLE, true, false))
+                               MEMMAP_BOOTLOADER_RECLAIMABLE, true, false, false))
             break;
         modules_mem_base -= 4096;
     }
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index cb827ae5..18b77caf 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -9,7 +9,6 @@
 #include <lib/config.h>
 #include <lib/time.h>
 #include <lib/print.h>
-#include <lib/rand.h>
 #include <lib/real.h>
 #include <lib/uri.h>
 #include <lib/fb.h>
@@ -23,8 +22,6 @@
 #include <mm/mtrr.h>
 #include <stivale/stivale.h>
 
-#define KASLR_SLIDE_BITMASK 0x000FFF000u
-
 struct stivale_struct stivale_struct = {0};
 
 void stivale_load(char *config, char *cmdline) {
@@ -50,9 +47,11 @@ void stivale_load(char *config, char *cmdline) {
 
     int ret;
 
+    bool level5pg = false;
+
     uint64_t slide = 0;
+    uint64_t entry_point = 0;
 
-    bool level5pg = false;
     switch (bits) {
         case 64: {
             // Check if 64 bit CPU
@@ -66,19 +65,21 @@ void stivale_load(char *config, char *cmdline) {
                 level5pg = true;
             }
 
-            char *s_kaslr = config_get_value(config, 0, "KASLR");
-            if (s_kaslr != NULL && !strcmp(s_kaslr, "yes")) {
-                // KASLR is enabled, set the slide
-                slide = rand64() & KASLR_SLIDE_BITMASK;
-            }
+            if (elf64_load(kernel, &entry_point, &slide, 10))
+                panic("stivale: ELF64 load failure");
 
             ret = elf64_load_section(kernel, &stivale_hdr, ".stivalehdr", sizeof(struct stivale_header), slide);
 
             break;
         }
-        case 32:
+        case 32: {
+            if (elf32_load(kernel, (uint32_t *)&entry_point, 10))
+                panic("stivale: ELF32 load failure");
+
             ret = elf32_load_section(kernel, &stivale_hdr, ".stivalehdr", sizeof(struct stivale_header));
+
             break;
+        }
         default:
             panic("stivale: Not 32 nor 64 bit x86 ELF file.");
     }
@@ -96,26 +97,13 @@ void stivale_load(char *config, char *cmdline) {
             panic("stivale: Section .stivalehdr is smaller than size of the struct.");
     }
 
-    print("stivale: Requested stack at %X\n", stivale_hdr.stack);
-
-    uint64_t entry_point   = 0;
-    uint64_t top_used_addr = 0;
-
-    switch (bits) {
-        case 64:
-            elf64_load(kernel, &entry_point, &top_used_addr, slide, 10);
-            break;
-        case 32:
-            elf32_load(kernel, (uint32_t *)&entry_point, (uint32_t *)&top_used_addr, 10);
-            break;
-    }
-
     if (stivale_hdr.entry_point != 0)
         entry_point = stivale_hdr.entry_point;
 
     print("stivale: Kernel slide: %X\n", slide);
 
-    print("stivale: Top used address in ELF: %X\n", top_used_addr);
+    print("stivale: Entry point at: %X\n", entry_point);
+    print("stivale: Requested stack at: %X\n", stivale_hdr.stack);
 
     stivale_struct.module_count = 0;
     uint64_t *prev_mod_ptr = &stivale_struct.modules;
diff --git a/stage23/protos/stivale2.c b/stage23/protos/stivale2.c
index cc99c552..80df8e19 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -68,35 +68,39 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
 
     int ret;
 
+    bool level5pg = false;
+
     uint64_t slide = 0;
+    uint64_t entry_point = 0;
 
-    bool level5pg = false;
     switch (bits) {
         case 64: {
             // Check if 64 bit CPU
             uint32_t eax, ebx, ecx, edx;
             if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
-                panic("stivale: This CPU does not support 64-bit mode.");
+                panic("stivale2: This CPU does not support 64-bit mode.");
             }
             // Check if 5-level paging is available
             if (cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 16))) {
-                print("stivale: CPU has 5-level paging support\n");
+                print("stivale2: CPU has 5-level paging support\n");
                 level5pg = true;
             }
 
-            char *s_kaslr = config_get_value(config, 0, "KASLR");
-            if (s_kaslr != NULL && !strcmp(s_kaslr, "yes")) {
-                // KASLR is enabled, set the slide
-                slide = rand64() & KASLR_SLIDE_BITMASK;
-            }
+            if (elf64_load(kernel, &entry_point, &slide, 10))
+                panic("stivale2: ELF64 load failure");
 
             ret = elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr", sizeof(struct stivale2_header), slide);
 
             break;
         }
-        case 32:
+        case 32: {
+            if (elf32_load(kernel, (uint32_t *)&entry_point, 10))
+                panic("stivale2: ELF32 load failure");
+
             ret = elf32_load_section(kernel, &stivale2_hdr, ".stivale2hdr", sizeof(struct stivale2_header));
+
             break;
+        }
         default:
             panic("stivale2: Not 32 nor 64 bit x86 ELF file.");
     }
@@ -114,26 +118,13 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
             panic("stivale2: Section .stivale2hdr is smaller than size of the struct.");
     }
 
-    print("stivale2: Requested stack at %X\n", stivale2_hdr.stack);
-
-    uint64_t entry_point   = 0;
-    uint64_t top_used_addr = 0;
-
-    switch (bits) {
-        case 64:
-            elf64_load(kernel, &entry_point, &top_used_addr, slide, 0x1001);
-            break;
-        case 32:
-            elf32_load(kernel, (uint32_t *)&entry_point, (uint32_t *)&top_used_addr, 0x1001);
-            break;
-    }
-
     if (stivale2_hdr.entry_point != 0)
         entry_point = stivale2_hdr.entry_point;
 
     print("stivale2: Kernel slide: %X\n", slide);
 
-    print("stivale2: Top used address in ELF: %X\n", top_used_addr);
+    print("stivale2: Entry point at: %X\n", entry_point);
+    print("stivale2: Requested stack at: %X\n", stivale2_hdr.stack);
 
     strcpy(stivale2_struct.bootloader_brand, "Limine");
     strcpy(stivale2_struct.bootloader_version, LIMINE_VERSION);
tab: 248 wrap: offon