All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 0/4] x86/KASLR: Randomize virtual address separately
@ 2016-05-10 17:19 ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This is v8 of the last 3 patches from v7, with an additional clean-up for
the pagetable.c code. The rest of the series has landed in -tip.

The patches are:
- 1: Further clean up on pagetable.c.
- 2: Last part of Baoquan's decoupling the physical address and virtual
     address randomization of kernel text.
- 3: Remove upper bound on physical address range.
- 4: Remove lower bound on physical address range.

Thanks for putting up with my attempts at this, and for all the review!
Things really do look much cleaner. :)

-Kees

v7->v8:
- extracted identity map initialization function to be part of the
  called interface, renamed appropriately to initialize_identity_maps().
- added copyright to pagetable.c for clarity.
- shuffled initialization of mapping_info around again for good measure.
- refactored remaining patches to include call to initialize_identity_maps().

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] [PATCH v8 0/4] x86/KASLR: Randomize virtual address separately
@ 2016-05-10 17:19 ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This is v8 of the last 3 patches from v7, with an additional clean-up for
the pagetable.c code. The rest of the series has landed in -tip.

The patches are:
- 1: Further clean up on pagetable.c.
- 2: Last part of Baoquan's decoupling the physical address and virtual
     address randomization of kernel text.
- 3: Remove upper bound on physical address range.
- 4: Remove lower bound on physical address range.

Thanks for putting up with my attempts at this, and for all the review!
Things really do look much cleaner. :)

-Kees

v7->v8:
- extracted identity map initialization function to be part of the
  called interface, renamed appropriately to initialize_identity_maps().
- added copyright to pagetable.c for clarity.
- shuffled initialization of mapping_info around again for good measure.
- refactored remaining patches to include call to initialize_identity_maps().

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-10 17:19 ` [kernel-hardening] " Kees Cook
@ 2016-05-10 17:19   ` Kees Cook
  -1 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This extracts the call to prepare_level4() into a top-level function
that the user of the pagetable.c interface must call to initialize
the new page tables. For clarity and to match the "finalize" function,
it has been renamed to initialize_identity_maps(). This function also
gains the initialization of mapping_info so we don't have to do it each
time in add_identity_map().

Additionally add copyright notice to the top, to make it clear that the
bulk of the pagetable.c code was written by Yinghai, and that I just
added bugs later. :)

Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c     |  3 +++
 arch/x86/boot/compressed/misc.h      |  3 +++
 arch/x86/boot/compressed/pagetable.c | 26 ++++++++++++++++----------
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index cfeb0259ed81..03a6f5d85a6b 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -485,6 +485,9 @@ unsigned char *choose_random_location(unsigned long input,
 
 	boot_params->hdr.loadflags |= KASLR_FLAG;
 
+	/* Prepare to add new identity pagetables on demand. */
+	initialize_identity_maps();
+
 	/* Record the various known unsafe memory ranges. */
 	mem_avoid_init(input, input_size, output);
 
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index b6fec1ff10e4..09c4ddd02ac6 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -85,10 +85,13 @@ unsigned char *choose_random_location(unsigned long input_ptr,
 #endif
 
 #ifdef CONFIG_X86_64
+void initialize_identity_maps(void);
 void add_identity_map(unsigned long start, unsigned long size);
 void finalize_identity_maps(void);
 extern unsigned char _pgtable[];
 #else
+static inline void initialize_identity_maps(void)
+{ }
 static inline void add_identity_map(unsigned long start, unsigned long size)
 { }
 static inline void finalize_identity_maps(void)
diff --git a/arch/x86/boot/compressed/pagetable.c b/arch/x86/boot/compressed/pagetable.c
index 34b95df14e69..ea4ee6abc0d2 100644
--- a/arch/x86/boot/compressed/pagetable.c
+++ b/arch/x86/boot/compressed/pagetable.c
@@ -2,6 +2,9 @@
  * This code is used on x86_64 to create page table identity mappings on
  * demand by building up a new set of page tables (or appending to the
  * existing ones), and then switching over to them when ready.
+ *
+ * Copyright (C) 2015-2016  Yinghai Lu
+ * Copyright (C)      2016  Kees Cook
  */
 
 /*
@@ -59,9 +62,21 @@ static struct alloc_pgt_data pgt_data;
 /* The top level page table entry pointer. */
 static unsigned long level4p;
 
+/*
+ * Mapping information structure passed to kernel_ident_mapping_init().
+ * Due to relocation, pointers must be assigned at run time not build time.
+ */
+static struct x86_mapping_info mapping_info = {
+	.pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
+};
+
 /* Locates and clears a region for a new top level page table. */
-static void prepare_level4(void)
+void initialize_identity_maps(void)
 {
+	/* Init mapping_info with run-time function/buffer pointers. */
+	mapping_info.alloc_pgt_page = alloc_pgt_page;
+	mapping_info.context = &pgt_data;
+
 	/*
 	 * It should be impossible for this not to already be true,
 	 * but since calling this a second time would rewind the other
@@ -96,17 +111,8 @@ static void prepare_level4(void)
  */
 void add_identity_map(unsigned long start, unsigned long size)
 {
-	struct x86_mapping_info mapping_info = {
-		.alloc_pgt_page	= alloc_pgt_page,
-		.context	= &pgt_data,
-		.pmd_flag	= __PAGE_KERNEL_LARGE_EXEC,
-	};
 	unsigned long end = start + size;
 
-	/* Make sure we have a top level page table ready to use. */
-	if (!level4p)
-		prepare_level4();
-
 	/* Align boundary to 2M. */
 	start = round_down(start, PMD_SIZE);
 	end = round_up(end, PMD_SIZE);
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [kernel-hardening] [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-10 17:19   ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This extracts the call to prepare_level4() into a top-level function
that the user of the pagetable.c interface must call to initialize
the new page tables. For clarity and to match the "finalize" function,
it has been renamed to initialize_identity_maps(). This function also
gains the initialization of mapping_info so we don't have to do it each
time in add_identity_map().

Additionally add copyright notice to the top, to make it clear that the
bulk of the pagetable.c code was written by Yinghai, and that I just
added bugs later. :)

Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c     |  3 +++
 arch/x86/boot/compressed/misc.h      |  3 +++
 arch/x86/boot/compressed/pagetable.c | 26 ++++++++++++++++----------
 3 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index cfeb0259ed81..03a6f5d85a6b 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -485,6 +485,9 @@ unsigned char *choose_random_location(unsigned long input,
 
 	boot_params->hdr.loadflags |= KASLR_FLAG;
 
+	/* Prepare to add new identity pagetables on demand. */
+	initialize_identity_maps();
+
 	/* Record the various known unsafe memory ranges. */
 	mem_avoid_init(input, input_size, output);
 
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index b6fec1ff10e4..09c4ddd02ac6 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -85,10 +85,13 @@ unsigned char *choose_random_location(unsigned long input_ptr,
 #endif
 
 #ifdef CONFIG_X86_64
+void initialize_identity_maps(void);
 void add_identity_map(unsigned long start, unsigned long size);
 void finalize_identity_maps(void);
 extern unsigned char _pgtable[];
 #else
+static inline void initialize_identity_maps(void)
+{ }
 static inline void add_identity_map(unsigned long start, unsigned long size)
 { }
 static inline void finalize_identity_maps(void)
diff --git a/arch/x86/boot/compressed/pagetable.c b/arch/x86/boot/compressed/pagetable.c
index 34b95df14e69..ea4ee6abc0d2 100644
--- a/arch/x86/boot/compressed/pagetable.c
+++ b/arch/x86/boot/compressed/pagetable.c
@@ -2,6 +2,9 @@
  * This code is used on x86_64 to create page table identity mappings on
  * demand by building up a new set of page tables (or appending to the
  * existing ones), and then switching over to them when ready.
+ *
+ * Copyright (C) 2015-2016  Yinghai Lu
+ * Copyright (C)      2016  Kees Cook
  */
 
 /*
@@ -59,9 +62,21 @@ static struct alloc_pgt_data pgt_data;
 /* The top level page table entry pointer. */
 static unsigned long level4p;
 
+/*
+ * Mapping information structure passed to kernel_ident_mapping_init().
+ * Due to relocation, pointers must be assigned at run time not build time.
+ */
+static struct x86_mapping_info mapping_info = {
+	.pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
+};
+
 /* Locates and clears a region for a new top level page table. */
-static void prepare_level4(void)
+void initialize_identity_maps(void)
 {
+	/* Init mapping_info with run-time function/buffer pointers. */
+	mapping_info.alloc_pgt_page = alloc_pgt_page;
+	mapping_info.context = &pgt_data;
+
 	/*
 	 * It should be impossible for this not to already be true,
 	 * but since calling this a second time would rewind the other
@@ -96,17 +111,8 @@ static void prepare_level4(void)
  */
 void add_identity_map(unsigned long start, unsigned long size)
 {
-	struct x86_mapping_info mapping_info = {
-		.alloc_pgt_page	= alloc_pgt_page,
-		.context	= &pgt_data,
-		.pmd_flag	= __PAGE_KERNEL_LARGE_EXEC,
-	};
 	unsigned long end = start + size;
 
-	/* Make sure we have a top level page table ready to use. */
-	if (!level4p)
-		prepare_level4();
-
 	/* Align boundary to 2M. */
 	start = round_down(start, PMD_SIZE);
 	end = round_up(end, PMD_SIZE);
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v8 2/4] x86/KASLR: Randomize virtual address separately
  2016-05-10 17:19 ` [kernel-hardening] " Kees Cook
@ 2016-05-10 17:19   ` Kees Cook
  -1 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Baoquan He, Borislav Petkov, Yinghai Lu, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

From: Baoquan He <bhe@redhat.com>

The current KASLR implementation randomizes the physical and virtual
addresses of the kernel together (both are offset by the same amount). It
calculates the delta of the physical address where vmlinux was linked
to load and where it is finally loaded. If the delta is not equal to 0
(i.e. the kernel was relocated), relocation handling needs be done.

On 64-bit, this patch randomizes both the physical address where kernel
is decompressed and the virtual address where kernel text is mapped and
will execute from. We now have two values being chosen, so the function
arguments are reorganized to pass by pointer so they can be directly
updated. Since relocation handling only depends on the virtual address,
we must check the virtual delta, not the physical delta for processing
kernel relocations. This also populates the page table for the new
virtual address range. 32-bit does not support a separate virtual address,
so it continues to use the physical offset for its virtual offset.

Additionally updates the sanity checks done on the resulting kernel
addresses since they are potentially separate now.

Signed-off-by: Baoquan He <bhe@redhat.com>
[kees: rewrote changelog, limited virtual split to 64-bit only, update checks]
[kees: fix CONFIG_RANDOMIZE_BASE=n boot failure]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c | 44 ++++++++++++++++++++----------------
 arch/x86/boot/compressed/misc.c  | 49 ++++++++++++++++++++++++----------------
 arch/x86/boot/compressed/misc.h  | 22 ++++++++++--------
 3 files changed, 66 insertions(+), 49 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 03a6f5d85a6b..af92ea581b8e 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -463,23 +463,26 @@ static unsigned long find_random_virt_addr(unsigned long minimum,
  * Since this function examines addresses much more numerically,
  * it takes the input and output pointers as 'unsigned long'.
  */
-unsigned char *choose_random_location(unsigned long input,
-				      unsigned long input_size,
-				      unsigned long output,
-				      unsigned long output_size)
+void choose_random_location(unsigned long input,
+			    unsigned long input_size,
+			    unsigned long *output,
+			    unsigned long output_size,
+			    unsigned long *virt_addr)
 {
-	unsigned long choice = output;
 	unsigned long random_addr;
 
+	/* By default, keep output position unchanged. */
+	*virt_addr = *output;
+
 #ifdef CONFIG_HIBERNATION
 	if (!cmdline_find_option_bool("kaslr")) {
 		warn("KASLR disabled: 'kaslr' not on cmdline (hibernation selected).");
-		goto out;
+		return;
 	}
 #else
 	if (cmdline_find_option_bool("nokaslr")) {
 		warn("KASLR disabled: 'nokaslr' on cmdline.");
-		goto out;
+		return;
 	}
 #endif
 
@@ -489,25 +492,26 @@ unsigned char *choose_random_location(unsigned long input,
 	initialize_identity_maps();
 
 	/* Record the various known unsafe memory ranges. */
-	mem_avoid_init(input, input_size, output);
+	mem_avoid_init(input, input_size, *output);
 
 	/* Walk e820 and find a random address. */
-	random_addr = find_random_phys_addr(output, output_size);
+	random_addr = find_random_phys_addr(*output, output_size);
 	if (!random_addr) {
 		warn("KASLR disabled: could not find suitable E820 region!");
-		goto out;
+	} else {
+		/* Update the new physical address location. */
+		if (*output != random_addr) {
+			add_identity_map(random_addr, output_size);
+			*output = random_addr;
+		}
 	}
 
-	/* Always enforce the minimum. */
-	if (random_addr < choice)
-		goto out;
-
-	choice = random_addr;
-
-	add_identity_map(choice, output_size);
-
 	/* This actually loads the identity pagetable on x86_64. */
 	finalize_identity_maps();
-out:
-	return (unsigned char *)choice;
+
+	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
+	if (IS_ENABLED(CONFIG_X86_64))
+		random_addr = find_random_virt_addr(LOAD_PHYSICAL_ADDR,
+						 output_size);
+	*virt_addr = random_addr;
 }
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index f14db4e21654..b3c5a5f030ce 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -170,7 +170,8 @@ void __puthex(unsigned long value)
 }
 
 #if CONFIG_X86_NEED_RELOCS
-static void handle_relocations(void *output, unsigned long output_len)
+static void handle_relocations(void *output, unsigned long output_len,
+			       unsigned long virt_addr)
 {
 	int *reloc;
 	unsigned long delta, map, ptr;
@@ -182,11 +183,6 @@ static void handle_relocations(void *output, unsigned long output_len)
 	 * and where it was actually loaded.
 	 */
 	delta = min_addr - LOAD_PHYSICAL_ADDR;
-	if (!delta) {
-		debug_putstr("No relocation needed... ");
-		return;
-	}
-	debug_putstr("Performing relocations... ");
 
 	/*
 	 * The kernel contains a table of relocation addresses. Those
@@ -198,6 +194,20 @@ static void handle_relocations(void *output, unsigned long output_len)
 	map = delta - __START_KERNEL_map;
 
 	/*
+	 * 32-bit always performs relocations. 64-bit relocations are only
+	 * needed if KASLR has chosen a different starting address offset
+	 * from __START_KERNEL_map.
+	 */
+	if (IS_ENABLED(CONFIG_X86_64))
+		delta = virt_addr - LOAD_PHYSICAL_ADDR;
+
+	if (!delta) {
+		debug_putstr("No relocation needed... ");
+		return;
+	}
+	debug_putstr("Performing relocations... ");
+
+	/*
 	 * Process relocations: 32 bit relocations first then 64 bit after.
 	 * Three sets of binary relocations are added to the end of the kernel
 	 * before compression. Each relocation table entry is the kernel
@@ -250,7 +260,8 @@ static void handle_relocations(void *output, unsigned long output_len)
 #endif
 }
 #else
-static inline void handle_relocations(void *output, unsigned long output_len)
+static inline void handle_relocations(void *output, unsigned long output_len,
+				      unsigned long virt_addr)
 { }
 #endif
 
@@ -327,7 +338,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 				  unsigned long output_len)
 {
 	const unsigned long kernel_total_size = VO__end - VO__text;
-	unsigned char *output_orig = output;
+	unsigned long virt_addr = (unsigned long)output;
 
 	/* Retain x86 boot parameters pointer passed from startup_32/64. */
 	boot_params = rmode;
@@ -366,13 +377,16 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 	 * the entire decompressed kernel plus relocation table, or the
 	 * entire decompressed kernel plus .bss and .brk sections.
 	 */
-	output = choose_random_location((unsigned long)input_data, input_len,
-					(unsigned long)output,
-					max(output_len, kernel_total_size));
+	choose_random_location((unsigned long)input_data, input_len,
+				(unsigned long *)&output,
+				max(output_len, kernel_total_size),
+				&virt_addr);
 
 	/* Validate memory location choices. */
 	if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
-		error("Destination address inappropriately aligned");
+		error("Destination physical address inappropriately aligned");
+	if (virt_addr & (MIN_KERNEL_ALIGN - 1))
+		error("Destination virtual address inappropriately aligned");
 #ifdef CONFIG_X86_64
 	if (heap > 0x3fffffffffffUL)
 		error("Destination address too large");
@@ -382,19 +396,16 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 #endif
 #ifndef CONFIG_RELOCATABLE
 	if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
-		error("Wrong destination address");
+		error("Destination address does not match LOAD_PHYSICAL_ADDR");
+	if ((unsigned long)output != virt_addr)
+		error("Destination virtual address changed when not relocatable");
 #endif
 
 	debug_putstr("\nDecompressing Linux... ");
 	__decompress(input_data, input_len, NULL, NULL, output, output_len,
 			NULL, error);
 	parse_elf(output);
-	/*
-	 * 32-bit always performs relocations. 64-bit relocations are only
-	 * needed if kASLR has chosen a different load address.
-	 */
-	if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
-		handle_relocations(output, output_len);
+	handle_relocations(output, output_len, virt_addr);
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 09c4ddd02ac6..1c8355eadbd1 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -67,20 +67,22 @@ int cmdline_find_option_bool(const char *option);
 
 #if CONFIG_RANDOMIZE_BASE
 /* kaslr.c */
-unsigned char *choose_random_location(unsigned long input_ptr,
-				      unsigned long input_size,
-				      unsigned long output_ptr,
-				      unsigned long output_size);
+void choose_random_location(unsigned long input,
+			    unsigned long input_size,
+			    unsigned long *output,
+			    unsigned long output_size,
+			    unsigned long *virt_addr);
 /* cpuflags.c */
 bool has_cpuflag(int flag);
 #else
-static inline
-unsigned char *choose_random_location(unsigned long input_ptr,
-				      unsigned long input_size,
-				      unsigned long output_ptr,
-				      unsigned long output_size)
+static inline void choose_random_location(unsigned long input,
+					  unsigned long input_size,
+					  unsigned long *output,
+					  unsigned long output_size,
+					  unsigned long *virt_addr)
 {
-	return (unsigned char *)output_ptr;
+	/* No change from existing output location. */
+	*virt_addr = *output;
 }
 #endif
 
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [kernel-hardening] [PATCH v8 2/4] x86/KASLR: Randomize virtual address separately
@ 2016-05-10 17:19   ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Baoquan He, Borislav Petkov, Yinghai Lu, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

From: Baoquan He <bhe@redhat.com>

The current KASLR implementation randomizes the physical and virtual
addresses of the kernel together (both are offset by the same amount). It
calculates the delta of the physical address where vmlinux was linked
to load and where it is finally loaded. If the delta is not equal to 0
(i.e. the kernel was relocated), relocation handling needs be done.

On 64-bit, this patch randomizes both the physical address where kernel
is decompressed and the virtual address where kernel text is mapped and
will execute from. We now have two values being chosen, so the function
arguments are reorganized to pass by pointer so they can be directly
updated. Since relocation handling only depends on the virtual address,
we must check the virtual delta, not the physical delta for processing
kernel relocations. This also populates the page table for the new
virtual address range. 32-bit does not support a separate virtual address,
so it continues to use the physical offset for its virtual offset.

Additionally updates the sanity checks done on the resulting kernel
addresses since they are potentially separate now.

Signed-off-by: Baoquan He <bhe@redhat.com>
[kees: rewrote changelog, limited virtual split to 64-bit only, update checks]
[kees: fix CONFIG_RANDOMIZE_BASE=n boot failure]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c | 44 ++++++++++++++++++++----------------
 arch/x86/boot/compressed/misc.c  | 49 ++++++++++++++++++++++++----------------
 arch/x86/boot/compressed/misc.h  | 22 ++++++++++--------
 3 files changed, 66 insertions(+), 49 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 03a6f5d85a6b..af92ea581b8e 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -463,23 +463,26 @@ static unsigned long find_random_virt_addr(unsigned long minimum,
  * Since this function examines addresses much more numerically,
  * it takes the input and output pointers as 'unsigned long'.
  */
-unsigned char *choose_random_location(unsigned long input,
-				      unsigned long input_size,
-				      unsigned long output,
-				      unsigned long output_size)
+void choose_random_location(unsigned long input,
+			    unsigned long input_size,
+			    unsigned long *output,
+			    unsigned long output_size,
+			    unsigned long *virt_addr)
 {
-	unsigned long choice = output;
 	unsigned long random_addr;
 
+	/* By default, keep output position unchanged. */
+	*virt_addr = *output;
+
 #ifdef CONFIG_HIBERNATION
 	if (!cmdline_find_option_bool("kaslr")) {
 		warn("KASLR disabled: 'kaslr' not on cmdline (hibernation selected).");
-		goto out;
+		return;
 	}
 #else
 	if (cmdline_find_option_bool("nokaslr")) {
 		warn("KASLR disabled: 'nokaslr' on cmdline.");
-		goto out;
+		return;
 	}
 #endif
 
@@ -489,25 +492,26 @@ unsigned char *choose_random_location(unsigned long input,
 	initialize_identity_maps();
 
 	/* Record the various known unsafe memory ranges. */
-	mem_avoid_init(input, input_size, output);
+	mem_avoid_init(input, input_size, *output);
 
 	/* Walk e820 and find a random address. */
-	random_addr = find_random_phys_addr(output, output_size);
+	random_addr = find_random_phys_addr(*output, output_size);
 	if (!random_addr) {
 		warn("KASLR disabled: could not find suitable E820 region!");
-		goto out;
+	} else {
+		/* Update the new physical address location. */
+		if (*output != random_addr) {
+			add_identity_map(random_addr, output_size);
+			*output = random_addr;
+		}
 	}
 
-	/* Always enforce the minimum. */
-	if (random_addr < choice)
-		goto out;
-
-	choice = random_addr;
-
-	add_identity_map(choice, output_size);
-
 	/* This actually loads the identity pagetable on x86_64. */
 	finalize_identity_maps();
-out:
-	return (unsigned char *)choice;
+
+	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
+	if (IS_ENABLED(CONFIG_X86_64))
+		random_addr = find_random_virt_addr(LOAD_PHYSICAL_ADDR,
+						 output_size);
+	*virt_addr = random_addr;
 }
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index f14db4e21654..b3c5a5f030ce 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -170,7 +170,8 @@ void __puthex(unsigned long value)
 }
 
 #if CONFIG_X86_NEED_RELOCS
-static void handle_relocations(void *output, unsigned long output_len)
+static void handle_relocations(void *output, unsigned long output_len,
+			       unsigned long virt_addr)
 {
 	int *reloc;
 	unsigned long delta, map, ptr;
@@ -182,11 +183,6 @@ static void handle_relocations(void *output, unsigned long output_len)
 	 * and where it was actually loaded.
 	 */
 	delta = min_addr - LOAD_PHYSICAL_ADDR;
-	if (!delta) {
-		debug_putstr("No relocation needed... ");
-		return;
-	}
-	debug_putstr("Performing relocations... ");
 
 	/*
 	 * The kernel contains a table of relocation addresses. Those
@@ -198,6 +194,20 @@ static void handle_relocations(void *output, unsigned long output_len)
 	map = delta - __START_KERNEL_map;
 
 	/*
+	 * 32-bit always performs relocations. 64-bit relocations are only
+	 * needed if KASLR has chosen a different starting address offset
+	 * from __START_KERNEL_map.
+	 */
+	if (IS_ENABLED(CONFIG_X86_64))
+		delta = virt_addr - LOAD_PHYSICAL_ADDR;
+
+	if (!delta) {
+		debug_putstr("No relocation needed... ");
+		return;
+	}
+	debug_putstr("Performing relocations... ");
+
+	/*
 	 * Process relocations: 32 bit relocations first then 64 bit after.
 	 * Three sets of binary relocations are added to the end of the kernel
 	 * before compression. Each relocation table entry is the kernel
@@ -250,7 +260,8 @@ static void handle_relocations(void *output, unsigned long output_len)
 #endif
 }
 #else
-static inline void handle_relocations(void *output, unsigned long output_len)
+static inline void handle_relocations(void *output, unsigned long output_len,
+				      unsigned long virt_addr)
 { }
 #endif
 
@@ -327,7 +338,7 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 				  unsigned long output_len)
 {
 	const unsigned long kernel_total_size = VO__end - VO__text;
-	unsigned char *output_orig = output;
+	unsigned long virt_addr = (unsigned long)output;
 
 	/* Retain x86 boot parameters pointer passed from startup_32/64. */
 	boot_params = rmode;
@@ -366,13 +377,16 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 	 * the entire decompressed kernel plus relocation table, or the
 	 * entire decompressed kernel plus .bss and .brk sections.
 	 */
-	output = choose_random_location((unsigned long)input_data, input_len,
-					(unsigned long)output,
-					max(output_len, kernel_total_size));
+	choose_random_location((unsigned long)input_data, input_len,
+				(unsigned long *)&output,
+				max(output_len, kernel_total_size),
+				&virt_addr);
 
 	/* Validate memory location choices. */
 	if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
-		error("Destination address inappropriately aligned");
+		error("Destination physical address inappropriately aligned");
+	if (virt_addr & (MIN_KERNEL_ALIGN - 1))
+		error("Destination virtual address inappropriately aligned");
 #ifdef CONFIG_X86_64
 	if (heap > 0x3fffffffffffUL)
 		error("Destination address too large");
@@ -382,19 +396,16 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
 #endif
 #ifndef CONFIG_RELOCATABLE
 	if ((unsigned long)output != LOAD_PHYSICAL_ADDR)
-		error("Wrong destination address");
+		error("Destination address does not match LOAD_PHYSICAL_ADDR");
+	if ((unsigned long)output != virt_addr)
+		error("Destination virtual address changed when not relocatable");
 #endif
 
 	debug_putstr("\nDecompressing Linux... ");
 	__decompress(input_data, input_len, NULL, NULL, output, output_len,
 			NULL, error);
 	parse_elf(output);
-	/*
-	 * 32-bit always performs relocations. 64-bit relocations are only
-	 * needed if kASLR has chosen a different load address.
-	 */
-	if (!IS_ENABLED(CONFIG_X86_64) || output != output_orig)
-		handle_relocations(output, output_len);
+	handle_relocations(output, output_len, virt_addr);
 	debug_putstr("done.\nBooting the kernel.\n");
 	return output;
 }
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 09c4ddd02ac6..1c8355eadbd1 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -67,20 +67,22 @@ int cmdline_find_option_bool(const char *option);
 
 #if CONFIG_RANDOMIZE_BASE
 /* kaslr.c */
-unsigned char *choose_random_location(unsigned long input_ptr,
-				      unsigned long input_size,
-				      unsigned long output_ptr,
-				      unsigned long output_size);
+void choose_random_location(unsigned long input,
+			    unsigned long input_size,
+			    unsigned long *output,
+			    unsigned long output_size,
+			    unsigned long *virt_addr);
 /* cpuflags.c */
 bool has_cpuflag(int flag);
 #else
-static inline
-unsigned char *choose_random_location(unsigned long input_ptr,
-				      unsigned long input_size,
-				      unsigned long output_ptr,
-				      unsigned long output_size)
+static inline void choose_random_location(unsigned long input,
+					  unsigned long input_size,
+					  unsigned long *output,
+					  unsigned long output_size,
+					  unsigned long *virt_addr)
 {
-	return (unsigned char *)output_ptr;
+	/* No change from existing output location. */
+	*virt_addr = *output;
 }
 #endif
 
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v8 3/4] x86/KASLR: Add physical address randomization >4G
  2016-05-10 17:19 ` [kernel-hardening] " Kees Cook
@ 2016-05-10 17:19   ` Kees Cook
  -1 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Baoquan He, Borislav Petkov, Yinghai Lu, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This patch exchanges the prior slots[] array for the new slot_areas[]
array, and lifts the limitation of KERNEL_IMAGE_SIZE on the physical
address offset for 64-bit. As before, process_e820_entry() walks
memory and populates slot_areas[], splitting on any detected mem_avoid
collisions.

Finally, since the slots[] array and its associated functions are not
needed any more, so they are removed.

Based on earlier patches by Baoquan He.

Cc: Baoquan He <bhe@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
This patch is pretty noisy due to the indentation change in the
e820 walker. I couldn't find a cleaner way to do this that didn't
make the final code LESS readable, unfortunately. So, the diff is
ugly, but I think the results are clean.
---
 arch/x86/Kconfig                 |  27 +++++----
 arch/x86/boot/compressed/kaslr.c | 115 +++++++++++++++++++++++----------------
 2 files changed, 85 insertions(+), 57 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5892d549596d..bba81cf02b69 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1943,21 +1943,26 @@ config RANDOMIZE_BASE
 	  attempts relying on knowledge of the location of kernel
 	  code internals.
 
-	  The kernel physical and virtual address can be randomized
-	  from 16MB up to 1GB on 64-bit and 512MB on 32-bit. (Note that
-	  using RANDOMIZE_BASE reduces the memory space available to
-	  kernel modules from 1.5GB to 1GB.)
+	  On 64-bit, the kernel physical and virtual addresses are
+	  randomized separately. The physical address will be anywhere
+	  between 16MB and the top of physical memory (up to 64TB). The
+	  virtual address will be randomized from 16MB up to 1GB (9 bits
+	  of entropy). Note that this also reduces the memory space
+	  available to kernel modules from 1.5GB to 1GB.
+
+	  On 32-bit, the kernel physical and virtual addresses are
+	  randomized together. They will be randomized from 16MB up to
+	  512MB (8 bits of entropy).
 
 	  Entropy is generated using the RDRAND instruction if it is
 	  supported. If RDTSC is supported, its value is mixed into
 	  the entropy pool as well. If neither RDRAND nor RDTSC are
-	  supported, then entropy is read from the i8254 timer.
-
-	  Since the kernel is built using 2GB addressing, and
-	  PHYSICAL_ALIGN must be at a minimum of 2MB, only 10 bits of
-	  entropy is theoretically possible. Currently, with the
-	  default value for PHYSICAL_ALIGN and due to page table
-	  layouts, 64-bit uses 9 bits of entropy and 32-bit uses 8 bits.
+	  supported, then entropy is read from the i8254 timer. The
+	  usable entropy is limited by the kernel being built using
+	  2GB addressing, and that PHYSICAL_ALIGN must be at a
+	  minimum of 2MB. As a result, only 10 bits of entropy are
+	  theoretically possible, but the implementations are further
+	  limited due to memory layouts.
 
 	  If CONFIG_HIBERNATE is also enabled, KASLR is disabled at boot
 	  time. To enable it, boot with "kaslr" on the kernel command
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index af92ea581b8e..d0a823df183b 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -132,17 +132,6 @@ enum mem_avoid_index {
 
 static struct mem_vector mem_avoid[MEM_AVOID_MAX];
 
-static bool mem_contains(struct mem_vector *region, struct mem_vector *item)
-{
-	/* Item at least partially before region. */
-	if (item->start < region->start)
-		return false;
-	/* Item at least partially after region. */
-	if (item->start + item->size > region->start + region->size)
-		return false;
-	return true;
-}
-
 static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two)
 {
 	/* Item one is entirely before item two. */
@@ -319,8 +308,6 @@ static bool mem_avoid_overlap(struct mem_vector *img,
 	return is_overlapping;
 }
 
-static unsigned long slots[KERNEL_IMAGE_SIZE / CONFIG_PHYSICAL_ALIGN];
-
 struct slot_area {
 	unsigned long addr;
 	int num;
@@ -351,36 +338,44 @@ static void store_slot_info(struct mem_vector *region, unsigned long image_size)
 	}
 }
 
-static void slots_append(unsigned long addr)
-{
-	/* Overflowing the slots list should be impossible. */
-	if (slot_max >= KERNEL_IMAGE_SIZE / CONFIG_PHYSICAL_ALIGN)
-		return;
-
-	slots[slot_max++] = addr;
-}
-
 static unsigned long slots_fetch_random(void)
 {
+	unsigned long slot;
+	int i;
+
 	/* Handle case of no slots stored. */
 	if (slot_max == 0)
 		return 0;
 
-	return slots[get_random_long("Physical") % slot_max];
+	slot = get_random_long("Physical") % slot_max;
+
+	for (i = 0; i < slot_area_index; i++) {
+		if (slot >= slot_areas[i].num) {
+			slot -= slot_areas[i].num;
+			continue;
+		}
+		return slot_areas[i].addr + slot * CONFIG_PHYSICAL_ALIGN;
+	}
+
+	if (i == slot_area_index)
+		debug_putstr("slots_fetch_random() failed!?\n");
+	return 0;
 }
 
 static void process_e820_entry(struct e820entry *entry,
 			       unsigned long minimum,
 			       unsigned long image_size)
 {
-	struct mem_vector region, img, overlap;
+	struct mem_vector region, overlap;
+	struct slot_area slot_area;
+	unsigned long start_orig;
 
 	/* Skip non-RAM entries. */
 	if (entry->type != E820_RAM)
 		return;
 
-	/* Ignore entries entirely above our maximum. */
-	if (entry->addr >= KERNEL_IMAGE_SIZE)
+	/* On 32-bit, ignore entries entirely above our maximum. */
+	if (IS_ENABLED(CONFIG_X86_32) && entry->addr >= KERNEL_IMAGE_SIZE)
 		return;
 
 	/* Ignore entries entirely below our minimum. */
@@ -390,31 +385,55 @@ static void process_e820_entry(struct e820entry *entry,
 	region.start = entry->addr;
 	region.size = entry->size;
 
-	/* Potentially raise address to minimum location. */
-	if (region.start < minimum)
-		region.start = minimum;
+	/* Give up if slot area array is full. */
+	while (slot_area_index < MAX_SLOT_AREA) {
+		start_orig = region.start;
 
-	/* Potentially raise address to meet alignment requirements. */
-	region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);
+		/* Potentially raise address to minimum location. */
+		if (region.start < minimum)
+			region.start = minimum;
 
-	/* Did we raise the address above the bounds of this e820 region? */
-	if (region.start > entry->addr + entry->size)
-		return;
+		/* Potentially raise address to meet alignment needs. */
+		region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);
 
-	/* Reduce size by any delta from the original address. */
-	region.size -= region.start - entry->addr;
+		/* Did we raise the address above this e820 region? */
+		if (region.start > entry->addr + entry->size)
+			return;
 
-	/* Reduce maximum size to fit end of image within maximum limit. */
-	if (region.start + region.size > KERNEL_IMAGE_SIZE)
-		region.size = KERNEL_IMAGE_SIZE - region.start;
+		/* Reduce size by any delta from the original address. */
+		region.size -= region.start - start_orig;
 
-	/* Walk each aligned slot and check for avoided areas. */
-	for (img.start = region.start, img.size = image_size ;
-	     mem_contains(&region, &img) ;
-	     img.start += CONFIG_PHYSICAL_ALIGN) {
-		if (mem_avoid_overlap(&img, &overlap))
-			continue;
-		slots_append(img.start);
+		/* On 32-bit, reduce region size to fit within max size. */
+		if (IS_ENABLED(CONFIG_X86_32) &&
+		    region.start + region.size > KERNEL_IMAGE_SIZE)
+			region.size = KERNEL_IMAGE_SIZE - region.start;
+
+		/* Return if region can't contain decompressed kernel */
+		if (region.size < image_size)
+			return;
+
+		/* If nothing overlaps, store the region and return. */
+		if (!mem_avoid_overlap(&region, &overlap)) {
+			store_slot_info(&region, image_size);
+			return;
+		}
+
+		/* Store beginning of region if holds at least image_size. */
+		if (overlap.start > region.start + image_size) {
+			struct mem_vector beginning;
+
+			beginning.start = region.start;
+			beginning.size = overlap.start - region.start;
+			store_slot_info(&beginning, image_size);
+		}
+
+		/* Return if overlap extends to or past end of region. */
+		if (overlap.start + overlap.size >= region.start + region.size)
+			return;
+
+		/* Clip off the overlapping region and start over. */
+		region.size -= overlap.start - region.start + overlap.size;
+		region.start = overlap.start + overlap.size;
 	}
 }
 
@@ -431,6 +450,10 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
 	for (i = 0; i < boot_params->e820_entries; i++) {
 		process_e820_entry(&boot_params->e820_map[i], minimum,
 				   image_size);
+		if (slot_area_index == MAX_SLOT_AREA) {
+			debug_putstr("Aborted e820 scan (slot_areas full)!\n");
+			break;
+		}
 	}
 
 	return slots_fetch_random();
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [kernel-hardening] [PATCH v8 3/4] x86/KASLR: Add physical address randomization >4G
@ 2016-05-10 17:19   ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Baoquan He, Borislav Petkov, Yinghai Lu, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

This patch exchanges the prior slots[] array for the new slot_areas[]
array, and lifts the limitation of KERNEL_IMAGE_SIZE on the physical
address offset for 64-bit. As before, process_e820_entry() walks
memory and populates slot_areas[], splitting on any detected mem_avoid
collisions.

Finally, since the slots[] array and its associated functions are not
needed any more, so they are removed.

Based on earlier patches by Baoquan He.

Cc: Baoquan He <bhe@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
---
This patch is pretty noisy due to the indentation change in the
e820 walker. I couldn't find a cleaner way to do this that didn't
make the final code LESS readable, unfortunately. So, the diff is
ugly, but I think the results are clean.
---
 arch/x86/Kconfig                 |  27 +++++----
 arch/x86/boot/compressed/kaslr.c | 115 +++++++++++++++++++++++----------------
 2 files changed, 85 insertions(+), 57 deletions(-)

diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 5892d549596d..bba81cf02b69 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1943,21 +1943,26 @@ config RANDOMIZE_BASE
 	  attempts relying on knowledge of the location of kernel
 	  code internals.
 
-	  The kernel physical and virtual address can be randomized
-	  from 16MB up to 1GB on 64-bit and 512MB on 32-bit. (Note that
-	  using RANDOMIZE_BASE reduces the memory space available to
-	  kernel modules from 1.5GB to 1GB.)
+	  On 64-bit, the kernel physical and virtual addresses are
+	  randomized separately. The physical address will be anywhere
+	  between 16MB and the top of physical memory (up to 64TB). The
+	  virtual address will be randomized from 16MB up to 1GB (9 bits
+	  of entropy). Note that this also reduces the memory space
+	  available to kernel modules from 1.5GB to 1GB.
+
+	  On 32-bit, the kernel physical and virtual addresses are
+	  randomized together. They will be randomized from 16MB up to
+	  512MB (8 bits of entropy).
 
 	  Entropy is generated using the RDRAND instruction if it is
 	  supported. If RDTSC is supported, its value is mixed into
 	  the entropy pool as well. If neither RDRAND nor RDTSC are
-	  supported, then entropy is read from the i8254 timer.
-
-	  Since the kernel is built using 2GB addressing, and
-	  PHYSICAL_ALIGN must be at a minimum of 2MB, only 10 bits of
-	  entropy is theoretically possible. Currently, with the
-	  default value for PHYSICAL_ALIGN and due to page table
-	  layouts, 64-bit uses 9 bits of entropy and 32-bit uses 8 bits.
+	  supported, then entropy is read from the i8254 timer. The
+	  usable entropy is limited by the kernel being built using
+	  2GB addressing, and that PHYSICAL_ALIGN must be at a
+	  minimum of 2MB. As a result, only 10 bits of entropy are
+	  theoretically possible, but the implementations are further
+	  limited due to memory layouts.
 
 	  If CONFIG_HIBERNATE is also enabled, KASLR is disabled at boot
 	  time. To enable it, boot with "kaslr" on the kernel command
diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index af92ea581b8e..d0a823df183b 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -132,17 +132,6 @@ enum mem_avoid_index {
 
 static struct mem_vector mem_avoid[MEM_AVOID_MAX];
 
-static bool mem_contains(struct mem_vector *region, struct mem_vector *item)
-{
-	/* Item at least partially before region. */
-	if (item->start < region->start)
-		return false;
-	/* Item at least partially after region. */
-	if (item->start + item->size > region->start + region->size)
-		return false;
-	return true;
-}
-
 static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two)
 {
 	/* Item one is entirely before item two. */
@@ -319,8 +308,6 @@ static bool mem_avoid_overlap(struct mem_vector *img,
 	return is_overlapping;
 }
 
-static unsigned long slots[KERNEL_IMAGE_SIZE / CONFIG_PHYSICAL_ALIGN];
-
 struct slot_area {
 	unsigned long addr;
 	int num;
@@ -351,36 +338,44 @@ static void store_slot_info(struct mem_vector *region, unsigned long image_size)
 	}
 }
 
-static void slots_append(unsigned long addr)
-{
-	/* Overflowing the slots list should be impossible. */
-	if (slot_max >= KERNEL_IMAGE_SIZE / CONFIG_PHYSICAL_ALIGN)
-		return;
-
-	slots[slot_max++] = addr;
-}
-
 static unsigned long slots_fetch_random(void)
 {
+	unsigned long slot;
+	int i;
+
 	/* Handle case of no slots stored. */
 	if (slot_max == 0)
 		return 0;
 
-	return slots[get_random_long("Physical") % slot_max];
+	slot = get_random_long("Physical") % slot_max;
+
+	for (i = 0; i < slot_area_index; i++) {
+		if (slot >= slot_areas[i].num) {
+			slot -= slot_areas[i].num;
+			continue;
+		}
+		return slot_areas[i].addr + slot * CONFIG_PHYSICAL_ALIGN;
+	}
+
+	if (i == slot_area_index)
+		debug_putstr("slots_fetch_random() failed!?\n");
+	return 0;
 }
 
 static void process_e820_entry(struct e820entry *entry,
 			       unsigned long minimum,
 			       unsigned long image_size)
 {
-	struct mem_vector region, img, overlap;
+	struct mem_vector region, overlap;
+	struct slot_area slot_area;
+	unsigned long start_orig;
 
 	/* Skip non-RAM entries. */
 	if (entry->type != E820_RAM)
 		return;
 
-	/* Ignore entries entirely above our maximum. */
-	if (entry->addr >= KERNEL_IMAGE_SIZE)
+	/* On 32-bit, ignore entries entirely above our maximum. */
+	if (IS_ENABLED(CONFIG_X86_32) && entry->addr >= KERNEL_IMAGE_SIZE)
 		return;
 
 	/* Ignore entries entirely below our minimum. */
@@ -390,31 +385,55 @@ static void process_e820_entry(struct e820entry *entry,
 	region.start = entry->addr;
 	region.size = entry->size;
 
-	/* Potentially raise address to minimum location. */
-	if (region.start < minimum)
-		region.start = minimum;
+	/* Give up if slot area array is full. */
+	while (slot_area_index < MAX_SLOT_AREA) {
+		start_orig = region.start;
 
-	/* Potentially raise address to meet alignment requirements. */
-	region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);
+		/* Potentially raise address to minimum location. */
+		if (region.start < minimum)
+			region.start = minimum;
 
-	/* Did we raise the address above the bounds of this e820 region? */
-	if (region.start > entry->addr + entry->size)
-		return;
+		/* Potentially raise address to meet alignment needs. */
+		region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);
 
-	/* Reduce size by any delta from the original address. */
-	region.size -= region.start - entry->addr;
+		/* Did we raise the address above this e820 region? */
+		if (region.start > entry->addr + entry->size)
+			return;
 
-	/* Reduce maximum size to fit end of image within maximum limit. */
-	if (region.start + region.size > KERNEL_IMAGE_SIZE)
-		region.size = KERNEL_IMAGE_SIZE - region.start;
+		/* Reduce size by any delta from the original address. */
+		region.size -= region.start - start_orig;
 
-	/* Walk each aligned slot and check for avoided areas. */
-	for (img.start = region.start, img.size = image_size ;
-	     mem_contains(&region, &img) ;
-	     img.start += CONFIG_PHYSICAL_ALIGN) {
-		if (mem_avoid_overlap(&img, &overlap))
-			continue;
-		slots_append(img.start);
+		/* On 32-bit, reduce region size to fit within max size. */
+		if (IS_ENABLED(CONFIG_X86_32) &&
+		    region.start + region.size > KERNEL_IMAGE_SIZE)
+			region.size = KERNEL_IMAGE_SIZE - region.start;
+
+		/* Return if region can't contain decompressed kernel */
+		if (region.size < image_size)
+			return;
+
+		/* If nothing overlaps, store the region and return. */
+		if (!mem_avoid_overlap(&region, &overlap)) {
+			store_slot_info(&region, image_size);
+			return;
+		}
+
+		/* Store beginning of region if holds at least image_size. */
+		if (overlap.start > region.start + image_size) {
+			struct mem_vector beginning;
+
+			beginning.start = region.start;
+			beginning.size = overlap.start - region.start;
+			store_slot_info(&beginning, image_size);
+		}
+
+		/* Return if overlap extends to or past end of region. */
+		if (overlap.start + overlap.size >= region.start + region.size)
+			return;
+
+		/* Clip off the overlapping region and start over. */
+		region.size -= overlap.start - region.start + overlap.size;
+		region.start = overlap.start + overlap.size;
 	}
 }
 
@@ -431,6 +450,10 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
 	for (i = 0; i < boot_params->e820_entries; i++) {
 		process_e820_entry(&boot_params->e820_map[i], minimum,
 				   image_size);
+		if (slot_area_index == MAX_SLOT_AREA) {
+			debug_putstr("Aborted e820 scan (slot_areas full)!\n");
+			break;
+		}
 	}
 
 	return slots_fetch_random();
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v8 4/4] x86/KASLR: Allow randomization below load address
  2016-05-10 17:19 ` [kernel-hardening] " Kees Cook
@ 2016-05-10 17:19   ` Kees Cook
  -1 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Yinghai Lu, Borislav Petkov, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

From: Yinghai Lu <yinghai@kernel.org>

Currently the physical randomization's lower boundary is the original
kernel load address. For bootloaders that load kernels into very high
memory (e.g. kexec), this means randomization takes place in a very small
window at the top of memory, ignoring the large region of physical memory
below the load address.

Since mem_avoid is already correctly tracking the regions that must be
avoided, this patch changes the minimum address to whatever is less:
512M (to conservatively avoid unknown things in lower memory) or the
load address. Now, for example, if the kernel is loaded at 8G, [512M,
8G) will be added into possible physical memory positions.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
[kees: rewrote changelog, refactor to use min()]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index d0a823df183b..304c5c369aff 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -492,7 +492,7 @@ void choose_random_location(unsigned long input,
 			    unsigned long output_size,
 			    unsigned long *virt_addr)
 {
-	unsigned long random_addr;
+	unsigned long random_addr, min_addr;
 
 	/* By default, keep output position unchanged. */
 	*virt_addr = *output;
@@ -517,8 +517,11 @@ void choose_random_location(unsigned long input,
 	/* Record the various known unsafe memory ranges. */
 	mem_avoid_init(input, input_size, *output);
 
+	/* Low end should be the smaller of 512M or initial location. */
+	min_addr = min(*output, 512UL << 20);
+
 	/* Walk e820 and find a random address. */
-	random_addr = find_random_phys_addr(*output, output_size);
+	random_addr = find_random_phys_addr(min_addr, output_size);
 	if (!random_addr) {
 		warn("KASLR disabled: could not find suitable E820 region!");
 	} else {
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [kernel-hardening] [PATCH v8 4/4] x86/KASLR: Allow randomization below load address
@ 2016-05-10 17:19   ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-10 17:19 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Kees Cook, Yinghai Lu, Borislav Petkov, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML

From: Yinghai Lu <yinghai@kernel.org>

Currently the physical randomization's lower boundary is the original
kernel load address. For bootloaders that load kernels into very high
memory (e.g. kexec), this means randomization takes place in a very small
window at the top of memory, ignoring the large region of physical memory
below the load address.

Since mem_avoid is already correctly tracking the regions that must be
avoided, this patch changes the minimum address to whatever is less:
512M (to conservatively avoid unknown things in lower memory) or the
load address. Now, for example, if the kernel is loaded at 8G, [512M,
8G) will be added into possible physical memory positions.

Signed-off-by: Yinghai Lu <yinghai@kernel.org>
[kees: rewrote changelog, refactor to use min()]
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 arch/x86/boot/compressed/kaslr.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index d0a823df183b..304c5c369aff 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -492,7 +492,7 @@ void choose_random_location(unsigned long input,
 			    unsigned long output_size,
 			    unsigned long *virt_addr)
 {
-	unsigned long random_addr;
+	unsigned long random_addr, min_addr;
 
 	/* By default, keep output position unchanged. */
 	*virt_addr = *output;
@@ -517,8 +517,11 @@ void choose_random_location(unsigned long input,
 	/* Record the various known unsafe memory ranges. */
 	mem_avoid_init(input, input_size, *output);
 
+	/* Low end should be the smaller of 512M or initial location. */
+	min_addr = min(*output, 512UL << 20);
+
 	/* Walk e820 and find a random address. */
-	random_addr = find_random_phys_addr(*output, output_size);
+	random_addr = find_random_phys_addr(min_addr, output_size);
 	if (!random_addr) {
 		warn("KASLR disabled: could not find suitable E820 region!");
 	} else {
-- 
2.6.3

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-10 17:19   ` [kernel-hardening] " Kees Cook
@ 2016-05-10 17:44     ` Borislav Petkov
  -1 siblings, 0 replies; 20+ messages in thread
From: Borislav Petkov @ 2016-05-10 17:44 UTC (permalink / raw)
  To: Kees Cook
  Cc: Ingo Molnar, Yinghai Lu, Baoquan He, Ingo Molnar, H. Peter Anvin,
	Vivek Goyal, Andy Lutomirski, lasse.collin, Andrew Morton,
	Dave Young, kernel-hardening, LKML

On Tue, May 10, 2016 at 10:19:12AM -0700, Kees Cook wrote:
> This extracts the call to prepare_level4() into a top-level function
> that the user of the pagetable.c interface must call to initialize
> the new page tables. For clarity and to match the "finalize" function,
> it has been renamed to initialize_identity_maps(). This function also
> gains the initialization of mapping_info so we don't have to do it each
> time in add_identity_map().
> 
> Additionally add copyright notice to the top, to make it clear that the
> bulk of the pagetable.c code was written by Yinghai, and that I just
> added bugs later. :)
> 
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  arch/x86/boot/compressed/kaslr.c     |  3 +++
>  arch/x86/boot/compressed/misc.h      |  3 +++
>  arch/x86/boot/compressed/pagetable.c | 26 ++++++++++++++++----------
>  3 files changed, 22 insertions(+), 10 deletions(-)

Exactly!

Acked-by: Borislav Petkov <bp@suse.de>

-- 
Regards/Gruss,
    Boris.

SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
-- 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-10 17:44     ` Borislav Petkov
  0 siblings, 0 replies; 20+ messages in thread
From: Borislav Petkov @ 2016-05-10 17:44 UTC (permalink / raw)
  To: Kees Cook
  Cc: Ingo Molnar, Yinghai Lu, Baoquan He, Ingo Molnar, H. Peter Anvin,
	Vivek Goyal, Andy Lutomirski, lasse.collin, Andrew Morton,
	Dave Young, kernel-hardening, LKML

On Tue, May 10, 2016 at 10:19:12AM -0700, Kees Cook wrote:
> This extracts the call to prepare_level4() into a top-level function
> that the user of the pagetable.c interface must call to initialize
> the new page tables. For clarity and to match the "finalize" function,
> it has been renamed to initialize_identity_maps(). This function also
> gains the initialization of mapping_info so we don't have to do it each
> time in add_identity_map().
> 
> Additionally add copyright notice to the top, to make it clear that the
> bulk of the pagetable.c code was written by Yinghai, and that I just
> added bugs later. :)
> 
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  arch/x86/boot/compressed/kaslr.c     |  3 +++
>  arch/x86/boot/compressed/misc.h      |  3 +++
>  arch/x86/boot/compressed/pagetable.c | 26 ++++++++++++++++----------
>  3 files changed, 22 insertions(+), 10 deletions(-)

Exactly!

Acked-by: Borislav Petkov <bp@suse.de>

-- 
Regards/Gruss,
    Boris.

SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
-- 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-10 17:19   ` [kernel-hardening] " Kees Cook
@ 2016-05-11  6:24     ` Ingo Molnar
  -1 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-11  6:24 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> +/*
> + * Mapping information structure passed to kernel_ident_mapping_init().
> + * Due to relocation, pointers must be assigned at run time not build time.
> + */
> +static struct x86_mapping_info mapping_info = {
> +	.pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> +};

> +void initialize_identity_maps(void)
>  {
> +	/* Init mapping_info with run-time function/buffer pointers. */
> +	mapping_info.alloc_pgt_page = alloc_pgt_page;
> +	mapping_info.context = &pgt_data;

Could you please outline the precise failure mode? What gets executed when, which 
pointer gets relocated and which not, and exactly when does it pose a problem, 
etc.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-11  6:24     ` Ingo Molnar
  0 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-11  6:24 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	lasse.collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> +/*
> + * Mapping information structure passed to kernel_ident_mapping_init().
> + * Due to relocation, pointers must be assigned at run time not build time.
> + */
> +static struct x86_mapping_info mapping_info = {
> +	.pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> +};

> +void initialize_identity_maps(void)
>  {
> +	/* Init mapping_info with run-time function/buffer pointers. */
> +	mapping_info.alloc_pgt_page = alloc_pgt_page;
> +	mapping_info.context = &pgt_data;

Could you please outline the precise failure mode? What gets executed when, which 
pointer gets relocated and which not, and exactly when does it pose a problem, 
etc.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-11  6:24     ` [kernel-hardening] " Ingo Molnar
@ 2016-05-11 15:23       ` Kees Cook
  -1 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-11 15:23 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML

On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> * Kees Cook <keescook@chromium.org> wrote:
>
>> +/*
>> + * Mapping information structure passed to kernel_ident_mapping_init().
>> + * Due to relocation, pointers must be assigned at run time not build time.
>> + */
>> +static struct x86_mapping_info mapping_info = {
>> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
>> +};
>
>> +void initialize_identity_maps(void)
>>  {
>> +     /* Init mapping_info with run-time function/buffer pointers. */
>> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
>> +     mapping_info.context = &pgt_data;
>
> Could you please outline the precise failure mode? What gets executed when, which
> pointer gets relocated and which not, and exactly when does it pose a problem,
> etc.

It's the issue described at the top of misc.c:

/*
 * WARNING!!
 * This code is compiled with -fPIC and it is relocated dynamically at
 * run time, but no relocation processing is performed. This means that
 * it is not safe to place pointers in static structures.
 */

Should this be repeated in each .c file maybe as a reminder?

If the rest of the patches look good, we could clean this up as a
following patch? What do you think?

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-11 15:23       ` Kees Cook
  0 siblings, 0 replies; 20+ messages in thread
From: Kees Cook @ 2016-05-11 15:23 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML

On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
>
> * Kees Cook <keescook@chromium.org> wrote:
>
>> +/*
>> + * Mapping information structure passed to kernel_ident_mapping_init().
>> + * Due to relocation, pointers must be assigned at run time not build time.
>> + */
>> +static struct x86_mapping_info mapping_info = {
>> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
>> +};
>
>> +void initialize_identity_maps(void)
>>  {
>> +     /* Init mapping_info with run-time function/buffer pointers. */
>> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
>> +     mapping_info.context = &pgt_data;
>
> Could you please outline the precise failure mode? What gets executed when, which
> pointer gets relocated and which not, and exactly when does it pose a problem,
> etc.

It's the issue described at the top of misc.c:

/*
 * WARNING!!
 * This code is compiled with -fPIC and it is relocated dynamically at
 * run time, but no relocation processing is performed. This means that
 * it is not safe to place pointers in static structures.
 */

Should this be repeated in each .c file maybe as a reminder?

If the rest of the patches look good, we could clean this up as a
following patch? What do you think?

-Kees

-- 
Kees Cook
Chrome OS & Brillo Security

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-11 15:23       ` [kernel-hardening] " Kees Cook
@ 2016-05-12  8:30         ` Ingo Molnar
  -1 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-12  8:30 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > * Kees Cook <keescook@chromium.org> wrote:
> >
> >> +/*
> >> + * Mapping information structure passed to kernel_ident_mapping_init().
> >> + * Due to relocation, pointers must be assigned at run time not build time.
> >> + */
> >> +static struct x86_mapping_info mapping_info = {
> >> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> >> +};
> >
> >> +void initialize_identity_maps(void)
> >>  {
> >> +     /* Init mapping_info with run-time function/buffer pointers. */
> >> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
> >> +     mapping_info.context = &pgt_data;
> >
> > Could you please outline the precise failure mode? What gets executed when, which
> > pointer gets relocated and which not, and exactly when does it pose a problem,
> > etc.
> 
> It's the issue described at the top of misc.c:
> 
> /*
>  * WARNING!!
>  * This code is compiled with -fPIC and it is relocated dynamically at
>  * run time, but no relocation processing is performed. This means that
>  * it is not safe to place pointers in static structures.

So I think this is still a bit confusing: what's the difference between 'relocated 
dynamically at run time' and 'relocation processing'?

So 'to relocate code' usually means the whole deal: 'copy code and fix up'.

The problem here is that we copy the code to an address not known at build time, 
but don't fix it up (because this is the fixup code), right?

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-12  8:30         ` Ingo Molnar
  0 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-12  8:30 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > * Kees Cook <keescook@chromium.org> wrote:
> >
> >> +/*
> >> + * Mapping information structure passed to kernel_ident_mapping_init().
> >> + * Due to relocation, pointers must be assigned at run time not build time.
> >> + */
> >> +static struct x86_mapping_info mapping_info = {
> >> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> >> +};
> >
> >> +void initialize_identity_maps(void)
> >>  {
> >> +     /* Init mapping_info with run-time function/buffer pointers. */
> >> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
> >> +     mapping_info.context = &pgt_data;
> >
> > Could you please outline the precise failure mode? What gets executed when, which
> > pointer gets relocated and which not, and exactly when does it pose a problem,
> > etc.
> 
> It's the issue described at the top of misc.c:
> 
> /*
>  * WARNING!!
>  * This code is compiled with -fPIC and it is relocated dynamically at
>  * run time, but no relocation processing is performed. This means that
>  * it is not safe to place pointers in static structures.

So I think this is still a bit confusing: what's the difference between 'relocated 
dynamically at run time' and 'relocation processing'?

So 'to relocate code' usually means the whole deal: 'copy code and fix up'.

The problem here is that we copy the code to an address not known at build time, 
but don't fix it up (because this is the fixup code), right?

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
  2016-05-11 15:23       ` [kernel-hardening] " Kees Cook
@ 2016-05-12  8:31         ` Ingo Molnar
  -1 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-12  8:31 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > * Kees Cook <keescook@chromium.org> wrote:
> >
> >> +/*
> >> + * Mapping information structure passed to kernel_ident_mapping_init().
> >> + * Due to relocation, pointers must be assigned at run time not build time.
> >> + */
> >> +static struct x86_mapping_info mapping_info = {
> >> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> >> +};
> >
> >> +void initialize_identity_maps(void)
> >>  {
> >> +     /* Init mapping_info with run-time function/buffer pointers. */
> >> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
> >> +     mapping_info.context = &pgt_data;
> >
> > Could you please outline the precise failure mode? What gets executed when, which
> > pointer gets relocated and which not, and exactly when does it pose a problem,
> > etc.
> 
> It's the issue described at the top of misc.c:
> 
> /*
>  * WARNING!!
>  * This code is compiled with -fPIC and it is relocated dynamically at
>  * run time, but no relocation processing is performed. This means that
>  * it is not safe to place pointers in static structures.
>  */

Btw., is there any way to detect such invalid pointers during build-time somehow? 
This is not the first such bug and I suspect it won't be the last one either.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [kernel-hardening] Re: [PATCH v8 1/4] x86/KASLR: Clarify identity map interface
@ 2016-05-12  8:31         ` Ingo Molnar
  0 siblings, 0 replies; 20+ messages in thread
From: Ingo Molnar @ 2016-05-12  8:31 UTC (permalink / raw)
  To: Kees Cook
  Cc: Borislav Petkov, Yinghai Lu, Baoquan He, Ingo Molnar,
	H. Peter Anvin, Borislav Petkov, Vivek Goyal, Andy Lutomirski,
	Lasse Collin, Andrew Morton, Dave Young, kernel-hardening, LKML


* Kees Cook <keescook@chromium.org> wrote:

> On Tue, May 10, 2016 at 11:24 PM, Ingo Molnar <mingo@kernel.org> wrote:
> >
> > * Kees Cook <keescook@chromium.org> wrote:
> >
> >> +/*
> >> + * Mapping information structure passed to kernel_ident_mapping_init().
> >> + * Due to relocation, pointers must be assigned at run time not build time.
> >> + */
> >> +static struct x86_mapping_info mapping_info = {
> >> +     .pmd_flag       = __PAGE_KERNEL_LARGE_EXEC,
> >> +};
> >
> >> +void initialize_identity_maps(void)
> >>  {
> >> +     /* Init mapping_info with run-time function/buffer pointers. */
> >> +     mapping_info.alloc_pgt_page = alloc_pgt_page;
> >> +     mapping_info.context = &pgt_data;
> >
> > Could you please outline the precise failure mode? What gets executed when, which
> > pointer gets relocated and which not, and exactly when does it pose a problem,
> > etc.
> 
> It's the issue described at the top of misc.c:
> 
> /*
>  * WARNING!!
>  * This code is compiled with -fPIC and it is relocated dynamically at
>  * run time, but no relocation processing is performed. This means that
>  * it is not safe to place pointers in static structures.
>  */

Btw., is there any way to detect such invalid pointers during build-time somehow? 
This is not the first such bug and I suspect it won't be the last one either.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2016-05-12  8:31 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-10 17:19 [PATCH v8 0/4] x86/KASLR: Randomize virtual address separately Kees Cook
2016-05-10 17:19 ` [kernel-hardening] " Kees Cook
2016-05-10 17:19 ` [PATCH v8 1/4] x86/KASLR: Clarify identity map interface Kees Cook
2016-05-10 17:19   ` [kernel-hardening] " Kees Cook
2016-05-10 17:44   ` Borislav Petkov
2016-05-10 17:44     ` [kernel-hardening] " Borislav Petkov
2016-05-11  6:24   ` Ingo Molnar
2016-05-11  6:24     ` [kernel-hardening] " Ingo Molnar
2016-05-11 15:23     ` Kees Cook
2016-05-11 15:23       ` [kernel-hardening] " Kees Cook
2016-05-12  8:30       ` Ingo Molnar
2016-05-12  8:30         ` [kernel-hardening] " Ingo Molnar
2016-05-12  8:31       ` Ingo Molnar
2016-05-12  8:31         ` [kernel-hardening] " Ingo Molnar
2016-05-10 17:19 ` [PATCH v8 2/4] x86/KASLR: Randomize virtual address separately Kees Cook
2016-05-10 17:19   ` [kernel-hardening] " Kees Cook
2016-05-10 17:19 ` [PATCH v8 3/4] x86/KASLR: Add physical address randomization >4G Kees Cook
2016-05-10 17:19   ` [kernel-hardening] " Kees Cook
2016-05-10 17:19 ` [PATCH v8 4/4] x86/KASLR: Allow randomization below load address Kees Cook
2016-05-10 17:19   ` [kernel-hardening] " Kees Cook

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.