Re: Randomize link of kernel, and unmap startup code
> This change relinks kernel objects randomly, and unmaps the bootcode > component of locore during boot. This makes gadgets harder to find. > > The current linker layout is: > > locore.o [bring-up code + asm runtime] > rest of kernel .o, in order supplied by config(8) > > The new linker layout is: > > locore.o [just the bring-up code] > gap.o > rest of kernel .o + new locore2.S [asm runtime], via sort -R > > The gap.o being use some discussion. This creates 5 random sized > gaps: > Few pages after locore.o .text > > resulting in the following layout: > > boot code > [few pages of gap] > endboot: > [partial page gap] > rest of .text - randomized order > [page-alignment] > [partial page gap] > .rodata > [page-alignment] > [partial page gap] > .data > [partial page gap] > .data > > When we supply the .o files to the linker in random order, their text > segments are placed in that random order. The .rodata/.data/.bss for > each of those is also placed in the same order into their respective > sections. > > Once the system is booted, we unmap the locore.o bring-up code and the > first few pages of gap. (Cannot be too early, must be after "codepatch") > > This bootcode is at a known location in KVA space. At known offsets > within this .o text object, there are pointers to other .o in > particular to main(). By unmapping this bootcode, the addresses of > gadgets in the remaining objects become unknown. Due to randomization > are not known. There is no text-segment knowledge anymore about where > these objects are. Obviously some leakage of KVA addresses occurs, > and cleanup will need to continue to ASLR more of those objects. > > There are a few mitigation strategies against BROP attack methodology. > One can be summarized as "never reuse an address space". If a freshly > linked kernel of this type was booted each time, we would be well on > the way to satisfying that. Then other migitations efforts come into > play. > > I've booted around 100 amd64 kernels, that is fairly well tested. i386 > hasn't been tested as well yet. Here is an updated version of the diff. Index: sys/conf/makegap.sh === RCS file: sys/conf/makegap.sh diff -N sys/conf/makegap.sh --- /dev/null 1 Jan 1970 00:00:00 - +++ sys/conf/makegap.sh 30 May 2017 12:46:19 - @@ -0,0 +1,32 @@ +#!/bin/sh - + +PADBYTE=$1 + +cat << __EOF__ +#include +#include + + .text + .align PAGE_SIZE, $PADBYTE + .space $RANDOM, $PADBYTE + .align PAGE_SIZE, $PADBYTE + + .globl endboot +_C_LABEL(endboot): + .space PAGE_SIZE, $PADBYTE + .space $RANDOM % PAGE_SIZE, $PADBYTE + .align 16, $PADBYTE + + /* +* Randomly bias future data, bss, and rodata objects, +* does not help for objects in locore.S though + */ + .data + .space $RANDOM % PAGE_SIZE, $PADBYTE + + .bss + .space $RANDOM % PAGE_SIZE, $PADBYTE + + .section .rodata + .space $RANDOM % PAGE_SIZE, $PADBYTE +__EOF__ Index: sys/arch/amd64/amd64/autoconf.c === RCS file: /cvs/src/sys/arch/amd64/amd64/autoconf.c,v retrieving revision 1.47 diff -u -p -u -r1.47 autoconf.c --- sys/arch/amd64/amd64/autoconf.c 8 Jun 2016 17:24:44 - 1.47 +++ sys/arch/amd64/amd64/autoconf.c 30 May 2017 12:48:46 - @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,18 @@ void aesni_setup(void); extern int amd64_has_aesni; #endif +void +unmap_startup(void) +{ + extern void *kernel_text, *endboot; + vaddr_t p = (vaddr_t)&kernel_text; + + do { + pmap_kremove(p, PAGE_SIZE); + p += PAGE_SIZE; + } while (p < (vaddr_t)&endboot); +} + /* * Determine i/o configuration for a machine. */ @@ -122,6 +135,8 @@ cpu_configure(void) lapic_set_lvt(); ioapic_enable(); #endif + + unmap_startup(); #ifdef MULTIPROCESSOR cpu_init_idle_pcbs(); Index: sys/arch/amd64/amd64/locore.S === RCS file: /cvs/src/sys/arch/amd64/amd64/locore.S,v retrieving revision 1.84 diff -u -p -u -r1.84 locore.S --- sys/arch/amd64/amd64/locore.S 6 Feb 2017 09:15:51 - 1.84 +++ sys/arch/amd64/amd64/locore.S 30 May 2017 12:51:20 - @@ -140,11 +140,6 @@ #define SET_CURPCB(reg)movqreg,CPUVAR(CURPCB) -/* XXX temporary kluge; these should not be here */ -/* Get definitions for IOM_BEGIN, IOM_END, and IOM_SIZE */ -#include - - /* * Initialization */ @@ -230,524 +225,11 @@ gdt64_start: .quad 0x00cf9200/* kernel DS */ gdt64_end: -farjmp64: - .long
Re: Randomize link of kernel, and unmap startup code
On Tue, May 30, 2017 at 02:16:45AM -0600, Theo de Raadt wrote: > This change relinks kernel objects randomly, and unmaps the bootcode > component of locore during boot. This makes gadgets harder to find. > > The current linker layout is: > > locore.o [bring-up code + asm runtime] > rest of kernel .o, in order supplied by config(8) > > The new linker layout is: > > locore.o [just the bring-up code] > gap.o > rest of kernel .o + new locore2.S [asm runtime], via sort -R > > The gap.o being use some discussion. This creates 5 random sized > gaps: > Few pages after locore.o .text > > resulting in the following layout: > > boot code > [few pages of gap] > endboot: > [partial page gap] > rest of .text - randomized order > [page-alignment] > [partial page gap] > .rodata > [page-alignment] > [partial page gap] > .data > [partial page gap] > .data > > When we supply the .o files to the linker in random order, their text > segments are placed in that random order. The .rodata/.data/.bss for > each of those is also placed in the same order into their respective > sections. > > Once the system is booted, we unmap the locore.o bring-up code and the > first few pages of gap. (Cannot be too early, must be after "codepatch") > > This bootcode is at a known location in KVA space. At known offsets > within this .o text object, there are pointers to other .o in > particular to main(). By unmapping this bootcode, the addresses of > gadgets in the remaining objects become unknown. Due to randomization > are not known. There is no text-segment knowledge anymore about where > these objects are. Obviously some leakage of KVA addresses occurs, > and cleanup will need to continue to ASLR more of those objects. > > There are a few mitigation strategies against BROP attack methodology. > One can be summarized as "never reuse an address space". If a freshly > linked kernel of this type was booted each time, we would be well on > the way to satisfying that. Then other migitations efforts come into > play. > > I've booted around 100 amd64 kernels, that is fairly well tested. i386 > hasn't been tested as well yet. > Not sure if you're looking for oks, but the diff reads ok to me. -ml > Index: arch/amd64/amd64/autoconf.c > === > RCS file: /cvs/src/sys/arch/amd64/amd64/autoconf.c,v > retrieving revision 1.47 > diff -u -p -u -r1.47 autoconf.c > --- arch/amd64/amd64/autoconf.c 8 Jun 2016 17:24:44 - 1.47 > +++ arch/amd64/amd64/autoconf.c 29 May 2017 13:04:34 - > @@ -59,6 +59,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -105,6 +106,21 @@ void aesni_setup(void); > extern int amd64_has_aesni; > #endif > > +void > +unmap_startup(void) > +{ > + extern void *kernel_text, *endboot; > + vaddr_t p; > + > + printf("unmap kernel init code %lx-%lx\n", > + (vaddr_t)&kernel_text, (vaddr_t)&endboot); > + p = (vaddr_t)&kernel_text; > + do { > + pmap_kremove(p, PAGE_SIZE); > + p += NBPG; > + } while (p < (vaddr_t)&endboot); > +} > + > /* > * Determine i/o configuration for a machine. > */ > @@ -122,6 +138,8 @@ cpu_configure(void) > lapic_set_lvt(); > ioapic_enable(); > #endif > + > + unmap_startup(); > > #ifdef MULTIPROCESSOR > cpu_init_idle_pcbs(); > Index: arch/amd64/amd64/locore.S > === > RCS file: /cvs/src/sys/arch/amd64/amd64/locore.S,v > retrieving revision 1.84 > diff -u -p -u -r1.84 locore.S > --- arch/amd64/amd64/locore.S 6 Feb 2017 09:15:51 - 1.84 > +++ arch/amd64/amd64/locore.S 29 May 2017 20:52:28 - > @@ -131,115 +131,13 @@ > > #include > > -#define SET_CURPROC(proc,cpu)\ > - movqCPUVAR(SELF),cpu; \ > - movqproc,CPUVAR(CURPROC) ; \ > - movqcpu,P_CPU(proc) > - > -#define GET_CURPCB(reg) movqCPUVAR(CURPCB),reg > -#define SET_CURPCB(reg) movqreg,CPUVAR(CURPCB) > - > - > /* XXX temporary kluge; these should not be here */ > /* Get definitions for IOM_BEGIN, IOM_END, and IOM_SIZE */ > #include > > - > -/* > - * Initialization > - */ > - .data > - > -#if NLAPIC > 0 > - .align NBPG, 0xcc > - .globl _C_LABEL(local_apic), _C_LABEL(lapic_id), _C_LABEL(lapic_tpr) > -_C_LABEL(local_apic): > - .space LAPIC_ID > -_C_LABEL(lapic_id): > - .long 0x > - .space LAPIC_TPRI-(LAPIC_ID+4) > -_C_LABEL(lapic_tpr): > - .space LAPIC_PPRI-LAPIC_TPRI > -_C_LABEL(lapic_ppr): > - .space LAPIC_ISR-LAPIC_PPRI > -_C_LABEL(lapic_isr): > - .space NBPG-LAPIC_ISR > -#endif > - > - .globl _C_LABEL(cpu_id),_C_LABEL(cpu
Randomize link of kernel, and unmap startup code
This change relinks kernel objects randomly, and unmaps the bootcode component of locore during boot. This makes gadgets harder to find. The current linker layout is: locore.o [bring-up code + asm runtime] rest of kernel .o, in order supplied by config(8) The new linker layout is: locore.o [just the bring-up code] gap.o rest of kernel .o + new locore2.S [asm runtime], via sort -R The gap.o being use some discussion. This creates 5 random sized gaps: Few pages after locore.o .text #include #include +#include #include #include @@ -105,6 +106,21 @@ void aesni_setup(void); extern int amd64_has_aesni; #endif +void +unmap_startup(void) +{ + extern void *kernel_text, *endboot; + vaddr_t p; + + printf("unmap kernel init code %lx-%lx\n", + (vaddr_t)&kernel_text, (vaddr_t)&endboot); + p = (vaddr_t)&kernel_text; + do { + pmap_kremove(p, PAGE_SIZE); + p += NBPG; + } while (p < (vaddr_t)&endboot); +} + /* * Determine i/o configuration for a machine. */ @@ -122,6 +138,8 @@ cpu_configure(void) lapic_set_lvt(); ioapic_enable(); #endif + + unmap_startup(); #ifdef MULTIPROCESSOR cpu_init_idle_pcbs(); Index: arch/amd64/amd64/locore.S === RCS file: /cvs/src/sys/arch/amd64/amd64/locore.S,v retrieving revision 1.84 diff -u -p -u -r1.84 locore.S --- arch/amd64/amd64/locore.S 6 Feb 2017 09:15:51 - 1.84 +++ arch/amd64/amd64/locore.S 29 May 2017 20:52:28 - @@ -131,115 +131,13 @@ #include -#define SET_CURPROC(proc,cpu) \ - movqCPUVAR(SELF),cpu; \ - movqproc,CPUVAR(CURPROC) ; \ - movqcpu,P_CPU(proc) - -#define GET_CURPCB(reg)movqCPUVAR(CURPCB),reg -#define SET_CURPCB(reg)movqreg,CPUVAR(CURPCB) - - /* XXX temporary kluge; these should not be here */ /* Get definitions for IOM_BEGIN, IOM_END, and IOM_SIZE */ #include - -/* - * Initialization - */ - .data - -#if NLAPIC > 0 - .align NBPG, 0xcc - .globl _C_LABEL(local_apic), _C_LABEL(lapic_id), _C_LABEL(lapic_tpr) -_C_LABEL(local_apic): - .space LAPIC_ID -_C_LABEL(lapic_id): - .long 0x - .space LAPIC_TPRI-(LAPIC_ID+4) -_C_LABEL(lapic_tpr): - .space LAPIC_PPRI-LAPIC_TPRI -_C_LABEL(lapic_ppr): - .space LAPIC_ISR-LAPIC_PPRI -_C_LABEL(lapic_isr): - .space NBPG-LAPIC_ISR -#endif - - .globl _C_LABEL(cpu_id),_C_LABEL(cpu_vendor) - .globl _C_LABEL(cpuid_level),_C_LABEL(cpu_feature) - .globl _C_LABEL(cpu_ebxfeature) - .globl _C_LABEL(cpu_ecxfeature),_C_LABEL(ecpu_ecxfeature) - .globl _C_LABEL(cpu_perf_eax) - .globl _C_LABEL(cpu_perf_ebx) - .globl _C_LABEL(cpu_perf_edx) - .globl _C_LABEL(cpu_apmi_edx) - .globl _C_LABEL(ssym),_C_LABEL(esym),_C_LABEL(boothowto) - .globl _C_LABEL(bootdev) - .globl _C_LABEL(bootinfo), _C_LABEL(bootinfo_size), _C_LABEL(atdevbase) - .globl _C_LABEL(proc0paddr),_C_LABEL(PTDpaddr) - .globl _C_LABEL(biosbasemem),_C_LABEL(biosextmem) - .globl _C_LABEL(bootapiver) - .globl _C_LABEL(pg_nx) -_C_LABEL(cpu_id): .long 0 # saved from `cpuid' instruction -_C_LABEL(cpu_feature): .long 0 # feature flags from 'cpuid' - # instruction -_C_LABEL(cpu_ebxfeature):.long 0 # ext. ebx feature flags from 'cpuid' -_C_LABEL(cpu_ecxfeature):.long 0 # ext. ecx feature flags from 'cpuid' -_C_LABEL(ecpu_ecxfeature):.long0 # extended ecx feature flags -_C_LABEL(cpu_perf_eax):.long 0 # arch. perf. mon. flags from 'cpuid' -_C_LABEL(cpu_perf_ebx):.long 0 # arch. perf. mon. flags from 'cpuid' -_C_LABEL(cpu_perf_edx):.long 0 # arch. perf. mon. flags from 'cpuid' -_C_LABEL(cpu_apmi_edx):.long 0 # adv. power mgmt. info. from 'cpuid' -_C_LABEL(cpuid_level): .long -1 # max. level accepted by 'cpuid' - # instruction -_C_LABEL(cpu_vendor): .space 16 # vendor string returned by `cpuid' - # instruction -_C_LABEL(ssym):.quad 0 # ptr to start of syms -_C_LABEL(esym):.quad 0 # ptr to end of syms -_C_LABEL(atdevbase): .quad 0 # location of start of iomem in virtual -_C_LABEL(bootapiver): .long 0 # /boot API version -_C_LABEL(bootdev): .long 0 # device we booted from -_C_LABEL(proc0paddr): .quad 0 -_C_LABEL(PTDpaddr):.quad 0 # paddr of PTD, for libkvm -#ifndef REALBASEMEM -_C_LABEL(biosbasemem): .long 0 # base memory reported by BIOS -#else -_C_LABEL(biosbasemem): .long R