Re: Randomize link of kernel, and unmap startup code

2017-05-30 Thread Theo de Raadt
> 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

2017-05-30 Thread Mike Larkin
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

2017-05-30 Thread Theo de Raadt
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