Module Name:    src
Committed By:   maxv
Date:           Sat Jul 21 06:09:14 UTC 2018

Modified Files:
        src/sys/arch/amd64/amd64: cpufunc.S locore.S machdep.c
        src/sys/arch/x86/include: cpu_rng.h pmap.h
        src/sys/arch/x86/x86: cpu_rng.c pmap.c

Log Message:
More ASLR. Randomize the location of the direct map at boot time on amd64.
This doesn't need "options KASLR" and works on GENERIC. Will soon be
enabled by default.

The location of the areas is abstracted in a slotspace structure. Ideally
we should always use this structure when touching the L4 slots, instead of
the current cocktail of global variables and constants.

machdep initializes the structure with the default values, and we then
randomize its dmap entry. Ideally machdep should randomize everything at
once, but in the case of the direct map its size is determined a little
later in the boot procedure, so we're forced to randomize its location
later too.


To generate a diff of this commit:
cvs rdiff -u -r1.32 -r1.33 src/sys/arch/amd64/amd64/cpufunc.S
cvs rdiff -u -r1.171 -r1.172 src/sys/arch/amd64/amd64/locore.S
cvs rdiff -u -r1.306 -r1.307 src/sys/arch/amd64/amd64/machdep.c
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/x86/include/cpu_rng.h
cvs rdiff -u -r1.80 -r1.81 src/sys/arch/x86/include/pmap.h
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/x86/x86/cpu_rng.c
cvs rdiff -u -r1.291 -r1.292 src/sys/arch/x86/x86/pmap.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/amd64/amd64/cpufunc.S
diff -u src/sys/arch/amd64/amd64/cpufunc.S:1.32 src/sys/arch/amd64/amd64/cpufunc.S:1.33
--- src/sys/arch/amd64/amd64/cpufunc.S:1.32	Sat Jul 14 14:29:40 2018
+++ src/sys/arch/amd64/amd64/cpufunc.S	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpufunc.S,v 1.32 2018/07/14 14:29:40 maxv Exp $	*/
+/*	$NetBSD: cpufunc.S,v 1.33 2018/07/21 06:09:13 maxv Exp $	*/
 
 /*
  * Copyright (c) 1998, 2007, 2008 The NetBSD Foundation, Inc.
@@ -383,6 +383,14 @@ ENTRY(rdpmc)
 	ret
 END(rdpmc)
 
+ENTRY(rdtsc)
+	xorq	%rax,%rax
+	rdtsc
+	shlq	$32,%rdx
+	orq	%rdx,%rax
+	ret
+END(rdtsc)
+
 ENTRY(breakpoint)
 	pushq	%rbp
 	movq	%rsp, %rbp

Index: src/sys/arch/amd64/amd64/locore.S
diff -u src/sys/arch/amd64/amd64/locore.S:1.171 src/sys/arch/amd64/amd64/locore.S:1.172
--- src/sys/arch/amd64/amd64/locore.S:1.171	Sat Jul 14 14:29:40 2018
+++ src/sys/arch/amd64/amd64/locore.S	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: locore.S,v 1.171 2018/07/14 14:29:40 maxv Exp $	*/
+/*	$NetBSD: locore.S,v 1.172 2018/07/21 06:09:13 maxv Exp $	*/
 
 /*
  * Copyright-o-rama!
@@ -975,6 +975,7 @@ longmode_hi:
 
 	pushq	%rdi
 	call	_C_LABEL(init_bootspace)
+	call	_C_LABEL(init_slotspace)
 	popq	%rdi
 	call	_C_LABEL(init_x86_64)
 	call 	_C_LABEL(main)

Index: src/sys/arch/amd64/amd64/machdep.c
diff -u src/sys/arch/amd64/amd64/machdep.c:1.306 src/sys/arch/amd64/amd64/machdep.c:1.307
--- src/sys/arch/amd64/amd64/machdep.c:1.306	Thu Jul 12 19:48:16 2018
+++ src/sys/arch/amd64/amd64/machdep.c	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: machdep.c,v 1.306 2018/07/12 19:48:16 maxv Exp $	*/
+/*	$NetBSD: machdep.c,v 1.307 2018/07/21 06:09:13 maxv Exp $	*/
 
 /*
  * Copyright (c) 1996, 1997, 1998, 2000, 2006, 2007, 2008, 2011
@@ -110,7 +110,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.306 2018/07/12 19:48:16 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.307 2018/07/21 06:09:13 maxv Exp $");
 
 #include "opt_modular.h"
 #include "opt_user_ldt.h"
@@ -262,6 +262,7 @@ paddr_t ldt_paddr;
 static struct vm_map module_map_store;
 extern struct vm_map *module_map;
 extern struct bootspace bootspace;
+extern struct slotspace slotspace;
 
 struct vm_map *phys_map = NULL;
 
@@ -322,6 +323,7 @@ int dump_seg_count_range(paddr_t, paddr_
 int dumpsys_seg(paddr_t, paddr_t);
 
 void init_bootspace(void);
+void init_slotspace(void);
 void init_x86_64(paddr_t);
 
 /*
@@ -1590,6 +1592,58 @@ init_bootspace(void)
 }
 
 void
+init_slotspace(void)
+{
+	memset(&slotspace, 0, sizeof(slotspace));
+
+	/* User. */
+	slotspace.area[SLAREA_USER].sslot = 0;
+	slotspace.area[SLAREA_USER].mslot = PDIR_SLOT_PTE;
+	slotspace.area[SLAREA_USER].nslot = PDIR_SLOT_PTE;
+	slotspace.area[SLAREA_USER].active = true;
+	slotspace.area[SLAREA_USER].dropmax = false;
+
+	/* PTE. */
+	slotspace.area[SLAREA_PTE].sslot = PDIR_SLOT_PTE;
+	slotspace.area[SLAREA_PTE].mslot = 1;
+	slotspace.area[SLAREA_PTE].nslot = 1;
+	slotspace.area[SLAREA_PTE].active = true;
+	slotspace.area[SLAREA_PTE].dropmax = false;
+
+	/* Main. */
+	slotspace.area[SLAREA_MAIN].sslot = PDIR_SLOT_KERN;
+	slotspace.area[SLAREA_MAIN].mslot = NKL4_MAX_ENTRIES;
+	slotspace.area[SLAREA_MAIN].nslot = 0 /* variable */;
+	slotspace.area[SLAREA_MAIN].active = true;
+	slotspace.area[SLAREA_MAIN].dropmax = false;
+
+#ifdef __HAVE_PCPU_AREA
+	/* Per-CPU. */
+	slotspace.area[SLAREA_PCPU].sslot = PDIR_SLOT_PCPU;
+	slotspace.area[SLAREA_PCPU].mslot = 1;
+	slotspace.area[SLAREA_PCPU].nslot = 1;
+	slotspace.area[SLAREA_PCPU].active = true;
+	slotspace.area[SLAREA_PCPU].dropmax = false;
+#endif
+
+#ifdef __HAVE_DIRECT_MAP
+	/* Direct Map. */
+	slotspace.area[SLAREA_DMAP].sslot = PDIR_SLOT_DIRECT;
+	slotspace.area[SLAREA_DMAP].mslot = NL4_SLOT_DIRECT+1;
+	slotspace.area[SLAREA_DMAP].nslot = 0 /* variable */;
+	slotspace.area[SLAREA_DMAP].active = false;
+	slotspace.area[SLAREA_DMAP].dropmax = true;
+#endif
+
+	/* Kernel. */
+	slotspace.area[SLAREA_KERN].sslot = L4_SLOT_KERNBASE;
+	slotspace.area[SLAREA_KERN].mslot = 1;
+	slotspace.area[SLAREA_KERN].nslot = 1;
+	slotspace.area[SLAREA_KERN].active = true;
+	slotspace.area[SLAREA_KERN].dropmax = false;
+}
+
+void
 init_x86_64(paddr_t first_avail)
 {
 	extern void consinit(void);

Index: src/sys/arch/x86/include/cpu_rng.h
diff -u src/sys/arch/x86/include/cpu_rng.h:1.1 src/sys/arch/x86/include/cpu_rng.h:1.2
--- src/sys/arch/x86/include/cpu_rng.h:1.1	Sat Feb 27 00:09:45 2016
+++ src/sys/arch/x86/include/cpu_rng.h	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: cpu_rng.h,v 1.1 2016/02/27 00:09:45 tls Exp $ */
+/* $NetBSD: cpu_rng.h,v 1.2 2018/07/21 06:09:13 maxv Exp $ */
 
 #ifndef _X86_CPU_RNG_H_
 #define _X86_CPU_RNG_H_
@@ -38,5 +38,6 @@ typedef uint64_t cpu_rng_t;
 
 bool cpu_rng_init(void);
 size_t cpu_rng(cpu_rng_t *);
+void cpu_earlyrng(void *, size_t);
 
 #endif /* _X86_CPU_RNG_H_ */

Index: src/sys/arch/x86/include/pmap.h
diff -u src/sys/arch/x86/include/pmap.h:1.80 src/sys/arch/x86/include/pmap.h:1.81
--- src/sys/arch/x86/include/pmap.h:1.80	Wed Jun 20 11:49:38 2018
+++ src/sys/arch/x86/include/pmap.h	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: pmap.h,v 1.80 2018/06/20 11:49:38 maxv Exp $	*/
+/*	$NetBSD: pmap.h,v 1.81 2018/07/21 06:09:13 maxv Exp $	*/
 
 /*
  * Copyright (c) 1997 Charles D. Cranor and Washington University.
@@ -154,6 +154,25 @@ struct bootspace {
 	vaddr_t emodule;
 };
 
+#define SLSPACE_NONE	0
+#define SLAREA_USER	1
+#define SLAREA_PTE	2
+#define SLAREA_MAIN	3
+#define SLAREA_PCPU	4
+#define SLAREA_DMAP	5
+#define SLAREA_KERN	6
+#define SLSPACE_NAREAS	7
+
+struct slotspace {
+	struct {
+		size_t sslot; /* start slot */
+		size_t nslot; /* # of slots */
+		size_t mslot; /* max # of slots */
+		bool active;  /* area is active */
+		bool dropmax; /* !resizable */
+	} area[SLSPACE_NAREAS];
+};
+
 #ifndef MAXGDTSIZ
 #define MAXGDTSIZ 65536 /* XXX */
 #endif

Index: src/sys/arch/x86/x86/cpu_rng.c
diff -u src/sys/arch/x86/x86/cpu_rng.c:1.5 src/sys/arch/x86/x86/cpu_rng.c:1.6
--- src/sys/arch/x86/x86/cpu_rng.c:1.5	Mon Feb 29 00:17:54 2016
+++ src/sys/arch/x86/x86/cpu_rng.c	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/* $NetBSD: cpu_rng.c,v 1.5 2016/02/29 00:17:54 riastradh Exp $ */
+/* $NetBSD: cpu_rng.c,v 1.6 2018/07/21 06:09:13 maxv Exp $ */
 
 /*-
  * Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -38,6 +38,7 @@
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/cpu.h>
+#include <sys/sha2.h>
 
 #include <x86/specialreg.h>
 
@@ -193,3 +194,52 @@ cpu_rng(cpu_rng_t *out)
 		panic("cpu_rng: unknown mode %d", (int)cpu_rng_mode);
 	}
 }
+
+/* -------------------------------------------------------------------------- */
+
+static uint64_t earlyrng_state;
+
+/*
+ * Small PRNG, that can be used very early. The only requirement is that
+ * cpu_probe got called before.
+ */
+void
+cpu_earlyrng(void *out, size_t sz)
+{
+	uint8_t digest[SHA512_DIGEST_LENGTH];
+	SHA512_CTX ctx;
+	cpu_rng_t buf[8];
+	uint64_t val;
+	int i;
+
+	bool has_rdseed = (cpu_feature[5] & CPUID_SEF_RDSEED) != 0;
+	bool has_rdrand = (cpu_feature[1] & CPUID2_RDRAND) != 0;
+
+	KASSERT(sz + sizeof(uint64_t) <= SHA512_DIGEST_LENGTH);
+
+	SHA512_Init(&ctx);
+
+	SHA512_Update(&ctx, (uint8_t *)&earlyrng_state, sizeof(earlyrng_state));
+	if (has_rdseed) {
+		for (i = 0; i < 8; i++) {
+			if (cpu_rng_rdseed(&buf[i]) == 0) {
+				break;
+			}
+		}
+		SHA512_Update(&ctx, (uint8_t *)buf, i * sizeof(cpu_rng_t));
+	} else if (has_rdrand) {
+		for (i = 0; i < 8; i++) {
+			if (cpu_rng_rdrand(&buf[i]) == 0) {
+				break;
+			}
+		}
+		SHA512_Update(&ctx, (uint8_t *)buf, i * sizeof(cpu_rng_t));
+	}
+	val = rdtsc();
+	SHA512_Update(&ctx, (uint8_t *)&val, sizeof(val));
+
+	SHA512_Final(digest, &ctx);
+
+	memcpy(out, digest, sz);
+	memcpy(&earlyrng_state, &digest[sz], sizeof(earlyrng_state));
+}

Index: src/sys/arch/x86/x86/pmap.c
diff -u src/sys/arch/x86/x86/pmap.c:1.291 src/sys/arch/x86/x86/pmap.c:1.292
--- src/sys/arch/x86/x86/pmap.c:1.291	Wed Jun 20 11:57:22 2018
+++ src/sys/arch/x86/x86/pmap.c	Sat Jul 21 06:09:13 2018
@@ -1,4 +1,4 @@
-/*	$NetBSD: pmap.c,v 1.291 2018/06/20 11:57:22 maxv Exp $	*/
+/*	$NetBSD: pmap.c,v 1.292 2018/07/21 06:09:13 maxv Exp $	*/
 
 /*
  * Copyright (c) 2008, 2010, 2016, 2017 The NetBSD Foundation, Inc.
@@ -170,7 +170,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.291 2018/06/20 11:57:22 maxv Exp $");
+__KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.292 2018/07/21 06:09:13 maxv Exp $");
 
 #include "opt_user_ldt.h"
 #include "opt_lockdebug.h"
@@ -199,6 +199,7 @@ __KERNEL_RCSID(0, "$NetBSD: pmap.c,v 1.2
 #include <machine/isa_machdep.h>
 #include <machine/cpuvar.h>
 #include <machine/cputypes.h>
+#include <machine/cpu_rng.h>
 
 #include <x86/pmap.h>
 #include <x86/pmap_pv.h>
@@ -372,6 +373,7 @@ static struct pmap kernel_pmap_store;	/*
 struct pmap *const kernel_pmap_ptr = &kernel_pmap_store;
 
 struct bootspace bootspace __read_mostly;
+struct slotspace slotspace __read_mostly;
 
 /*
  * pmap_pg_nx: if our processor supports PG_NX in the PTE then we
@@ -501,8 +503,6 @@ static struct pool_cache pmap_pv_cache;
 #ifdef __HAVE_DIRECT_MAP
 vaddr_t pmap_direct_base __read_mostly;
 vaddr_t pmap_direct_end __read_mostly;
-size_t pmap_direct_pdpe __read_mostly;
-size_t pmap_direct_npdp __read_mostly;
 #endif
 
 #ifndef __HAVE_DIRECT_MAP
@@ -1394,6 +1394,80 @@ pmap_pagetree_nentries_range(vaddr_t sta
 }
 #endif
 
+#if defined(__HAVE_DIRECT_MAP)
+static inline void
+slotspace_copy(int type, pd_entry_t *dst, pd_entry_t *src)
+{
+	size_t sslot = slotspace.area[type].sslot;
+	size_t nslot = slotspace.area[type].nslot;
+
+	memcpy(&dst[sslot], &src[sslot], nslot * sizeof(pd_entry_t));
+}
+#endif
+
+#if defined(__HAVE_DIRECT_MAP) && defined(X86ASLR)
+/*
+ * Randomize the location of an area. We count the holes in the VM space. We
+ * randomly select one hole, and then randomly select an area within that hole.
+ * Finally we update the associated entry in the slotspace structure.
+ */
+static vaddr_t
+slotspace_rand(int type, size_t sz, size_t align)
+{
+	struct {
+		int start;
+		int end;
+	} holes[SLSPACE_NAREAS+1];
+	size_t i, nholes, hole;
+	size_t startsl, endsl, nslots, winsize;
+	vaddr_t startva, va;
+
+	sz = roundup(sz, align);
+	nslots = roundup(sz+NBPD_L4, NBPD_L4) / NBPD_L4;
+
+	/* Get the holes. */
+	nholes = 0;
+	for (i = 0; i < SLSPACE_NAREAS-1; i++) {
+		startsl = slotspace.area[i].sslot;
+		if (slotspace.area[i].active)
+			startsl += slotspace.area[i].mslot;
+		endsl = slotspace.area[i+1].sslot;
+		if (endsl - startsl >= nslots) {
+			holes[nholes].start = startsl;
+			holes[nholes].end = endsl;
+			nholes++;
+		}
+	}
+	if (nholes == 0) {
+		panic("%s: impossible", __func__);
+	}
+
+	/* Select a hole. */
+	cpu_earlyrng(&hole, sizeof(hole));
+	hole %= nholes;
+	startsl = holes[hole].start;
+	endsl = holes[hole].end;
+	startva = VA_SIGN_NEG(startsl * NBPD_L4);
+
+	/* Select an area within the hole. */
+	cpu_earlyrng(&va, sizeof(va));
+	winsize = ((endsl - startsl) * NBPD_L4) - sz;
+	va %= winsize;
+	va = rounddown(va, align);
+	va += startva;
+
+	/* Update the entry. */
+	slotspace.area[type].sslot = pl4_i(va);
+	slotspace.area[type].nslot =
+	    pmap_pagetree_nentries_range(va, va+sz, NBPD_L4);
+	if (slotspace.area[type].dropmax) {
+		slotspace.area[type].mslot = slotspace.area[type].nslot;
+	}
+
+	return va;
+}
+#endif
+
 #ifdef __HAVE_PCPU_AREA
 static void
 pmap_init_pcpu(void)
@@ -1494,7 +1568,7 @@ pmap_init_directmap(struct pmap *kpm)
 	extern phys_ram_seg_t mem_clusters[];
 	extern int mem_cluster_cnt;
 
-	const vaddr_t startva = PMAP_DIRECT_DEFAULT_BASE;
+	vaddr_t startva;
 	size_t nL4e, nL3e, nL2e;
 	size_t L4e_idx, L3e_idx, L2e_idx;
 	size_t spahole, epahole;
@@ -1526,6 +1600,12 @@ pmap_init_directmap(struct pmap *kpm)
 	if (lastpa > MAXPHYSMEM) {
 		panic("pmap_init_directmap: lastpa incorrect");
 	}
+
+#ifdef X86ASLR
+	startva = slotspace_rand(SLAREA_DMAP, lastpa, NBPD_L2);
+#else
+	startva = PMAP_DIRECT_DEFAULT_BASE;
+#endif
 	endva = startva + lastpa;
 
 	/* We will use this temporary va. */
@@ -1583,8 +1663,6 @@ pmap_init_directmap(struct pmap *kpm)
 
 	pmap_direct_base = startva;
 	pmap_direct_end = endva;
-	pmap_direct_pdpe = L4e_idx;
-	pmap_direct_npdp = nL4e;
 
 	tlbflush();
 }
@@ -2294,8 +2372,7 @@ pmap_pdp_ctor(void *arg, void *v, int fl
 	pdir[PDIR_SLOT_PCPU] = PDP_BASE[PDIR_SLOT_PCPU];
 #endif
 #ifdef __HAVE_DIRECT_MAP
-	memcpy(&pdir[pmap_direct_pdpe], &PDP_BASE[pmap_direct_pdpe],
-	    pmap_direct_npdp * sizeof(pd_entry_t));
+	slotspace_copy(SLAREA_DMAP, pdir, PDP_BASE);
 #endif
 #endif /* XEN  && __x86_64__*/
 

Reply via email to