:: commit 6dd399ea2b8db6d72f9f901a894813f187856fd0

N00byEdge <hannesbredberg@gmail.com> — 2020-05-29 09:05

parents: bb642c1970

Add KASLR for PIE ELF files when using stivale

diff --git a/STIVALE.md b/STIVALE.md
index 4f65e4e0..e70da03f 100644
--- a/STIVALE.md
+++ b/STIVALE.md
@@ -104,6 +104,8 @@ struct stivale_header {
                       // bit 1  0 = 4-level paging, 1 = use 5-level paging (if
                                                         available)
                                 Ignored if booting a 32-bit kernel.
+                      // bit 2  0 = Disable KASLR, 1 = enable KASLR (up to 1GB slide)
+                                Ignored if booting a 32-bit or non-relocatable kernel
                       // All other bits undefined.
 
     uint16_t framebuffer_width;   // These 3 values are parsed if a graphics mode
diff --git a/src/lib/elf.c b/src/lib/elf.c
index 8e4375f4..08228033 100644
--- a/src/lib/elf.c
+++ b/src/lib/elf.c
@@ -14,6 +14,9 @@
 #define ARCH_X86_64 0x3e
 #define ARCH_X86_32 0x03
 #define BITS_LE     0x01
+#define ET_DYN      0x0003
+#define SHT_RELA    0x00000004
+#define R_X86_64_RELATIVE 0x00000008
 
 /* Indices into identification array */
 #define EI_CLASS    4
@@ -103,6 +106,13 @@ struct elf32_shdr {
     uint32_t sh_entsize;
 };
 
+struct elf64_rela {
+    uint64_t r_addr;
+    uint32_t r_info;
+    uint32_t r_symbol;
+    uint64_t r_addend;
+};
+
 int elf_bits(struct file_handle *fd) {
     struct elf64_hdr hdr;
     fread(fd, &hdr, 0, 20);
@@ -122,7 +132,57 @@ int elf_bits(struct file_handle *fd) {
     }
 }
 
-int elf64_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit) {
+static int elf64_apply_relocations(struct file_handle *fd, struct elf64_hdr *hdr, void *buffer, uint64_t vaddr, size_t size, uint64_t slide) {
+    if (hdr->type != ET_DYN)
+        return 0; // Nothing to do if the ELF is not relocatable
+
+    // 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");
+            return 1;
+        }
+
+        // This is a RELA header, get and apply all relocations
+        for (uint64_t offset = 0; offset < section.sh_size; offset += section.sh_entsize) {
+            struct elf64_rela relocation;
+            fread(fd, &relocation, section.sh_offset + offset, section.sh_size);
+
+            switch (relocation.r_info) {
+                case R_X86_64_RELATIVE:
+                    // Relocation is before buffer
+                    if (relocation.r_addr < vaddr)
+                        continue;
+
+                    // Relocation is after buffer
+                    if (vaddr + size < relocation.r_addr + 8)
+                        continue;
+
+                    // It's inside it, calculate where it is
+                    uint64_t *ptr = (uint64_t *)((uint8_t *)buffer - vaddr + relocation.r_addr);
+
+                    // Write the relocated value
+                    *ptr = slide + relocation.r_addend;
+                    break;
+
+                default:
+                    print("elf: Unknown RELA type: %X\n", relocation.r_info);
+                    return 1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+int elf64_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit, uint64_t slide) {
     struct elf64_hdr hdr;
     fread(fd, &hdr, 0, sizeof(struct elf64_hdr));
 
@@ -157,7 +217,7 @@ int elf64_load_section(struct file_handle *fd, void *buffer, const char *name, s
             if (section.sh_size > limit)
                 return 3;
             fread(fd, buffer, section.sh_offset, section.sh_size);
-            return 0;
+            return elf64_apply_relocations(fd, &hdr, buffer, section.sh_addr, section.sh_size, slide);
         }
     }
 
@@ -208,7 +268,7 @@ int elf32_load_section(struct file_handle *fd, void *buffer, const char *name, s
 
 #define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
 
-int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top) {
+int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide) {
     struct elf64_hdr hdr;
     fread(fd, &hdr, 0, sizeof(struct elf64_hdr));
 
@@ -237,26 +297,37 @@ int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top) {
         if (phdr.p_type != PT_LOAD)
             continue;
 
-        if (phdr.p_vaddr & ((uint64_t)1 << 63))
-            phdr.p_vaddr -= FIXED_HIGHER_HALF_OFFSET_64;
+        uint64_t load_vaddr = phdr.p_vaddr;
+
+        if (load_vaddr & ((uint64_t)1 << 63))
+            load_vaddr -= FIXED_HIGHER_HALF_OFFSET_64;
+
+        load_vaddr += slide;
+
+        uint64_t this_top = load_vaddr + phdr.p_memsz;
 
-        uint64_t this_top = phdr.p_vaddr + phdr.p_memsz;
         if (this_top > *top)
             *top = this_top;
 
-        is_valid_memory_range((size_t)phdr.p_vaddr, (size_t)phdr.p_memsz);
+        is_valid_memory_range((size_t)load_vaddr, (size_t)phdr.p_memsz);
 
-        fread(fd, (void *)(uint32_t)phdr.p_vaddr, phdr.p_offset, phdr.p_filesz);
+        fread(fd, (void *)(uint32_t)load_vaddr, phdr.p_offset, phdr.p_filesz);
 
         size_t to_zero = (size_t)(phdr.p_memsz - phdr.p_filesz);
 
         if (to_zero) {
-            void *ptr = (void *)(uint32_t)(phdr.p_vaddr + phdr.p_filesz);
+            void *ptr = (void *)(uint32_t)(load_vaddr + phdr.p_filesz);
             memset(ptr, 0, to_zero);
         }
+
+        if (elf64_apply_relocations(fd, &hdr, (void *)(uint32_t)load_vaddr, phdr.p_vaddr, phdr.p_memsz, slide))
+            return -1;
     }
 
-    *entry_point = hdr.entry;
+    if (hdr.type == ET_DYN)
+        *entry_point = hdr.entry + slide;
+    else
+        *entry_point = hdr.entry;
 
     return 0;
 }
diff --git a/src/lib/elf.h b/src/lib/elf.h
index 0d8d4bc6..8f146def 100644
--- a/src/lib/elf.h
+++ b/src/lib/elf.h
@@ -6,8 +6,8 @@
 
 int elf_bits(struct file_handle *fd);
 
-int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top);
-int elf64_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit);
+int elf64_load(struct file_handle *fd, uint64_t *entry_point, uint64_t *top, uint64_t slide);
+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);
 int elf32_load_section(struct file_handle *fd, void *buffer, const char *name, size_t limit);
diff --git a/src/lib/random.c b/src/lib/random.c
new file mode 100644
index 00000000..dccb2656
--- /dev/null
+++ b/src/lib/random.c
@@ -0,0 +1,48 @@
+#include <lib/blib.h>
+
+int rdrand_available = 0;
+
+static void check_rdrand(void) {
+    uint32_t eax, ebx, ecx, edx;
+    int ret = cpuid(1, 0, &eax, &ebx, &ecx, &edx);
+    if (ret)
+        return;
+
+    if (ecx & (1 << 30))
+        rdrand_available = 1;
+}
+
+static void init_simple_rand(void) {
+    // TODO: Some fallback randomness init
+}
+
+void init_random(void) {
+    check_rdrand();
+    if (!rdrand_available) {
+        init_simple_rand();
+    }
+}
+
+static uint32_t rdrand(void) {
+    uint32_t val;
+
+    asm (
+        "1:rdrand %0\n\t"
+        "jnc 1b\n\t"
+        :"=r"(val)
+    );
+
+    return val;
+}
+
+static uint32_t simple_rand(void) {
+    // TODO: Some fallback randomness
+    return 0xFEEDFACE;
+}
+
+uint32_t get_random(void) {
+    if (rdrand_available)
+        return rdrand();
+    else
+        return simple_rand();
+}
diff --git a/src/lib/random.h b/src/lib/random.h
new file mode 100644
index 00000000..45a788b6
--- /dev/null
+++ b/src/lib/random.h
@@ -0,0 +1,10 @@
+#ifndef __LIB__RANDOM_H__
+#define __LIB__RANDOM_H__
+
+#include <stdint.h>
+
+void init_random(void);
+
+uint32_t get_random(void);
+
+#endif
diff --git a/src/main.c b/src/main.c
index 7807463a..18c2f8d6 100644
--- a/src/main.c
+++ b/src/main.c
@@ -22,6 +22,7 @@ asm (
 #include <lib/config.h>
 #include <lib/e820.h>
 #include <lib/print.h>
+#include <lib/random.h>
 #include <fs/file.h>
 #include <lib/elf.h>
 #include <protos/stivale.h>
@@ -98,6 +99,7 @@ refresh:
 void main(int boot_drive) {
     // Initial prompt.
     init_vga_textmode();
+    init_random();
 
     print("qloader2\n\n");
 
diff --git a/src/protos/stivale.c b/src/protos/stivale.c
index d7e32f32..6fa320e9 100644
--- a/src/protos/stivale.c
+++ b/src/protos/stivale.c
@@ -9,6 +9,7 @@
 #include <lib/config.h>
 #include <lib/time.h>
 #include <lib/print.h>
+#include <lib/random.h>
 #include <drivers/vbe.h>
 #include <drivers/vga_textmode.h>
 #include <fs/file.h>
@@ -46,6 +47,8 @@ struct stivale_struct {
     uint64_t flags;       // bit 0: 1 if booted with BIOS, 0 if booted with UEFI
 } __attribute__((packed));
 
+#define KASLR_SLIDE_BITMASK 0x03FFFF000u
+
 struct stivale_struct stivale_struct = {0};
 
 void stivale_load(char *cmdline, int boot_drive) {
@@ -85,6 +88,8 @@ void stivale_load(char *cmdline, int boot_drive) {
 
     int ret;
 
+    uint64_t slide = 0;
+
     bool level5pg = false;
     switch (bits) {
         case 64: {
@@ -100,7 +105,17 @@ void stivale_load(char *cmdline, int boot_drive) {
                 print("stivale: CPU has 5-level paging support\n");
                 level5pg = true;
             }
-            ret = elf64_load_section(fd, &stivale_hdr, ".stivalehdr", sizeof(struct stivale_header));
+
+            ret = elf64_load_section(fd, &stivale_hdr, ".stivalehdr", sizeof(struct stivale_header), slide);
+
+            if (!ret && ((stivale_hdr.flags >> 2) & 1)) {
+                // KASLR is enabled, set the slide
+                slide = get_random() & KASLR_SLIDE_BITMASK;
+
+                // Re-read the .stivalehdr with slid relocations
+                ret = elf64_load_section(fd, &stivale_hdr, ".stivalehdr", sizeof(struct stivale_header), slide);
+            }
+
             break;
         }
         case 32:
@@ -125,15 +140,18 @@ void stivale_load(char *cmdline, int boot_drive) {
 
     uint64_t entry_point   = 0;
     uint64_t top_used_addr = 0;
+
     switch (bits) {
         case 64:
-            elf64_load(fd, &entry_point, &top_used_addr);
+            elf64_load(fd, &entry_point, &top_used_addr, slide);
             break;
         case 32:
             elf32_load(fd, (uint32_t *)&entry_point, (uint32_t *)&top_used_addr);
             break;
     }
 
+    print("stivale: Kernel slide: %X\n", slide);
+
     print("stivale: Top used address in ELF: %X\n", top_used_addr);
 
     stivale_struct.memory_map_entries = (uint64_t)e820_entries;
tab: 248 wrap: offon