Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/amd64/stand/prekern Randomize the kernel segments i...



details:   https://anonhg.NetBSD.org/src/rev/d4d877b40525
branches:  trunk
changeset: 827466:d4d877b40525
user:      maxv <maxv%NetBSD.org@localhost>
date:      Sun Oct 29 11:28:30 2017 +0000

description:
Randomize the kernel segments independently. That is to say, put text,
rodata and data at different addresses (and in a random order).

To achieve that, the mapping order in the prekern is changed. Until now,
we were creating the kernel map the following way:
        -> choose a random VA
        -> map [kernpa_start; kernpa_end[ at this VA
        -> parse the ELF structures from there
        -> determine where exactly the kernel segments are located
        -> relocate etc
Now, we are doing:
        -> create a read-only view of [kernpa_start; kernpa_end[
        -> from this view, compute the size of the "head" region
        -> choose a random VA in the HEAD window, and map the head there
        -> for each region in (text, rodata, data, boot)
                -> compute the size of the region from the RO view
                -> choose a random VA in the KASLR window
                -> map the region there
        -> relocate etc

Each time we map a region, we initialize its bootspace fields right away.

The "head" region must be put before the other regions in memory, because
the kernel uses (headva + sh_offset) to get the addresses of the symbols,
and the offset is unsigned.

Given that the head does not have an mcmodel constraint, its location is
randomized in a window located below the KASLR window.

The rest of the regions being in the same window, we need to detect
collisions.

Note that the module map is embedded in the "boot" region, and that
therefore its location is randomized too.

diffstat:

 sys/arch/amd64/stand/prekern/elf.c     |  306 ++++++++++++++++++++++----------
 sys/arch/amd64/stand/prekern/locore.S  |    7 +-
 sys/arch/amd64/stand/prekern/mm.c      |  226 ++++++++++++++++++++---
 sys/arch/amd64/stand/prekern/prekern.c |   48 +----
 sys/arch/amd64/stand/prekern/prekern.h |   21 +-
 5 files changed, 427 insertions(+), 181 deletions(-)

diffs (truncated from 798 to 300 lines):

diff -r 347ac97b1660 -r d4d877b40525 sys/arch/amd64/stand/prekern/elf.c
--- a/sys/arch/amd64/stand/prekern/elf.c        Sun Oct 29 10:25:28 2017 +0000
+++ b/sys/arch/amd64/stand/prekern/elf.c        Sun Oct 29 11:28:30 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: elf.c,v 1.3 2017/10/29 10:07:08 maxv Exp $     */
+/*     $NetBSD: elf.c,v 1.4 2017/10/29 11:28:30 maxv Exp $     */
 
 /*
  * Copyright (c) 2017 The NetBSD Foundation, Inc. All rights reserved.
@@ -56,6 +56,8 @@
        } data;
 };
 
+extern paddr_t kernpa_start, kernpa_end;
+
 static struct elfinfo eif;
 static const char entrypoint[] = "start_prekern";
 
@@ -256,6 +258,37 @@
        }
 }
 
+/* -------------------------------------------------------------------------- */
+
+size_t
+elf_get_head_size(vaddr_t headva)
+{
+       Elf_Ehdr *ehdr;
+       Elf_Shdr *shdr;
+       size_t size;
+
+       ehdr = (Elf_Ehdr *)headva;
+       shdr = (Elf_Shdr *)((uint8_t *)ehdr + ehdr->e_shoff);
+
+       size = (vaddr_t)shdr + (vaddr_t)(ehdr->e_shnum * sizeof(Elf_Shdr)) -
+           (vaddr_t)ehdr;
+
+       return roundup(size, PAGE_SIZE);
+}
+
+void
+elf_build_head(vaddr_t headva)
+{
+       memset(&eif, 0, sizeof(struct elfinfo));
+
+       eif.ehdr = (Elf_Ehdr *)headva;
+       eif.shdr = (Elf_Shdr *)((uint8_t *)eif.ehdr + eif.ehdr->e_shoff);
+
+       if (elf_check_header() == -1) {
+               fatal("elf_build_info: wrong kernel ELF header");
+       }
+}
+
 static bool
 elf_section_is_text(Elf_Shdr *shdr)
 {
@@ -296,20 +329,180 @@
        return true;
 }
 
-static void
-elf_build_info(vaddr_t baseva)
+void
+elf_get_text(paddr_t *pa, size_t *sz)
+{
+       const paddr_t basepa = kernpa_start;
+       paddr_t minpa, maxpa, secpa;
+       size_t i, secsz;
+
+       minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_text(&eif.shdr[i])) {
+                       continue;
+               }
+               secpa = basepa + eif.shdr[i].sh_offset;
+               secsz = eif.shdr[i].sh_size;
+               if (secpa < minpa) {
+                       minpa = secpa;
+               }
+               if (secpa + secsz > maxpa) {
+                       maxpa = secpa + secsz;
+               }
+       }
+       ASSERT(minpa % PAGE_SIZE == 0);
+
+       *pa = minpa;
+       *sz = roundup(maxpa - minpa, PAGE_SIZE);
+}
+
+void
+elf_build_text(vaddr_t textva, paddr_t textpa, size_t textsz)
 {
-       vaddr_t secva, minva, maxva;
-       size_t secsz;
-       size_t i, j;
+       const paddr_t basepa = kernpa_start;
+       const vaddr_t headva = (vaddr_t)eif.ehdr;
+       size_t i, offtext;
+
+       eif.text.va = textva;
+       eif.text.sz = textsz;
+
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_text(&eif.shdr[i])) {
+                       continue;
+               }
+
+               /* Offset of the section within the text segment. */
+               offtext = basepa + eif.shdr[i].sh_offset - textpa;
+
+               /* We want (headva + sh_offset) to be the VA of the section. */
+               eif.shdr[i].sh_offset = (eif.text.va + offtext - headva);
+       }
+}
+
+void
+elf_get_rodata(paddr_t *pa, size_t *sz)
+{
+       const paddr_t basepa = kernpa_start;
+       paddr_t minpa, maxpa, secpa;
+       size_t i, secsz;
+
+       minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_rodata(&eif.shdr[i])) {
+                       continue;
+               }
+               secpa = basepa + eif.shdr[i].sh_offset;
+               secsz = eif.shdr[i].sh_size;
+               if (secpa < minpa) {
+                       minpa = secpa;
+               }
+               if (secpa + secsz > maxpa) {
+                       maxpa = secpa + secsz;
+               }
+       }
+       ASSERT(minpa % PAGE_SIZE == 0);
+
+       *pa = minpa;
+       *sz = roundup(maxpa - minpa, PAGE_SIZE);
+}
+
+void
+elf_build_rodata(vaddr_t rodatava, paddr_t rodatapa, size_t rodatasz)
+{
+       const paddr_t basepa = kernpa_start;
+       const vaddr_t headva = (vaddr_t)eif.ehdr;
+       size_t i, offrodata;
+
+       eif.rodata.va = rodatava;
+       eif.rodata.sz = rodatasz;
 
-       memset(&eif, 0, sizeof(struct elfinfo));
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_rodata(&eif.shdr[i])) {
+                       continue;
+               }
+
+               /* Offset of the section within the rodata segment. */
+               offrodata = basepa + eif.shdr[i].sh_offset - rodatapa;
+
+               /* We want (headva + sh_offset) to be the VA of the section. */
+               eif.shdr[i].sh_offset = (eif.rodata.va + offrodata - headva);
+       }
+}
+
+void
+elf_get_data(paddr_t *pa, size_t *sz)
+{
+       const paddr_t basepa = kernpa_start;
+       paddr_t minpa, maxpa, secpa;
+       size_t i, secsz;
+
+       minpa = 0xFFFFFFFFFFFFFFFF, maxpa = 0;
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_data(&eif.shdr[i])) {
+                       continue;
+               }
+               secpa = basepa + eif.shdr[i].sh_offset;
+               secsz = eif.shdr[i].sh_size;
+               if (secpa < minpa) {
+                       minpa = secpa;
+               }
+               if (secpa + secsz > maxpa) {
+                       maxpa = secpa + secsz;
+               }
+       }
+       ASSERT(minpa % PAGE_SIZE == 0);
+
+       *pa = minpa;
+       *sz = roundup(maxpa - minpa, PAGE_SIZE);
+}
 
-       eif.ehdr = (Elf_Ehdr *)baseva;
-       eif.shdr = (Elf_Shdr *)((uint8_t *)eif.ehdr + eif.ehdr->e_shoff);
+void
+elf_build_data(vaddr_t datava, paddr_t datapa, size_t datasz)
+{
+       const paddr_t basepa = kernpa_start;
+       const vaddr_t headva = (vaddr_t)eif.ehdr;
+       size_t i, offdata;
+
+       eif.data.va = datava;
+       eif.data.sz = datasz;
+
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (!elf_section_is_data(&eif.shdr[i])) {
+                       continue;
+               }
+
+               /* Offset of the section within the data segment. */
+               offdata = basepa + eif.shdr[i].sh_offset - datapa;
+
+               /* We want (headva + sh_offset) to be the VA of the section. */
+               eif.shdr[i].sh_offset = (eif.data.va + offdata - headva);
+       }
+}
 
-       if (elf_check_header(&eif) == -1) {
-               fatal("elf_build_info: wrong kernel ELF header");
+void
+elf_build_boot(vaddr_t bootva, paddr_t bootpa)
+{
+       const paddr_t basepa = kernpa_start;
+       const vaddr_t headva = (vaddr_t)eif.ehdr;
+       size_t i, j, offboot;
+
+       for (i = 0; i < eif.ehdr->e_shnum; i++) {
+               if (eif.shdr[i].sh_type != SHT_STRTAB &&
+                   eif.shdr[i].sh_type != SHT_REL &&
+                   eif.shdr[i].sh_type != SHT_RELA &&
+                   eif.shdr[i].sh_type != SHT_SYMTAB) {
+                       continue;
+               }
+               if (eif.shdr[i].sh_offset == 0) {
+                       /* hasn't been loaded */
+                       continue;
+               }
+
+               /* Offset of the section within the boot region. */
+               offboot = basepa + eif.shdr[i].sh_offset - bootpa;
+
+               /* We want (headva + sh_offset) to be the VA of the region. */
+               eif.shdr[i].sh_offset = (bootva + offboot - headva);
        }
 
        /* Locate the section names */
@@ -344,80 +537,16 @@
        }
        eif.strtab = (char *)((uint8_t *)eif.ehdr + eif.shdr[j].sh_offset);
        eif.strsz = eif.shdr[j].sh_size;
-
-       /*
-        * Save the locations of the kernel segments. Attention: there is a
-        * difference between "segment" and "section". A segment can contain
-        * several sections.
-        */
-
-       /* text */
-       minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
-       for (i = 0; i < eif.ehdr->e_shnum; i++) {
-               if (!elf_section_is_text(&eif.shdr[i])) {
-                       continue;
-               }
-               secva = baseva + eif.shdr[i].sh_offset;
-               secsz = eif.shdr[i].sh_size;
-               if (secva < minva) {
-                       minva = secva;
-               }
-               if (secva + secsz > maxva) {
-                       maxva = secva + secsz;
-               }
-       }
-       eif.text.va = minva;
-       eif.text.sz = roundup(maxva - minva, PAGE_SIZE);
-       ASSERT(eif.text.va % PAGE_SIZE == 0);
-
-       /* rodata */
-       minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
-       for (i = 0; i < eif.ehdr->e_shnum; i++) {
-               if (!elf_section_is_rodata(&eif.shdr[i])) {
-                       continue;
-               }
-               secva = baseva + eif.shdr[i].sh_offset;
-               secsz = eif.shdr[i].sh_size;
-               if (secva < minva) {
-                       minva = secva;
-               }
-               if (secva + secsz > maxva) {
-                       maxva = secva + secsz;
-               }
-       }
-       eif.rodata.va = minva;
-       eif.rodata.sz = roundup(maxva - minva, PAGE_SIZE);
-       ASSERT(eif.rodata.va % PAGE_SIZE == 0);
-
-       /* data */
-       minva = 0xFFFFFFFFFFFFFFFF, maxva = 0;
-       for (i = 0; i < eif.ehdr->e_shnum; i++) {
-               if (!elf_section_is_data(&eif.shdr[i])) {



Home | Main Index | Thread Index | Old Index