[PATCH v3 3/3] x86/64/KASLR: Determine the kernel mapping size at run time

From: Baoquan He
Date: Wed Jan 04 2017 - 03:50:14 EST


Kernel behaviour is inconsistent between KASLR code is not compiled in
with CONFIG_RANDOMIZE_BASE disabled and user specifies "nokaslr" with
CONFIG_RANDOMIZE_BASE enabled. As long as CONFIG_RANDOMIZE_BASE is
enabled, kernel mapping size will be extended up another 512M to 1G
though "nokaslr" is specified explicitly. This is buggy.
CONFIG_RANDOMIZE_BASE should only decide if KASLR code need be compiled
in. If user specify "nokaslr", the kernel have to behave as no KASLR
code compiled in at all as expected.

The root cause of the inconsistency is KERNEL_MAPPING_SIZE, the size of
kernel image mapping area, is fixed at compiling time, and is changed
from 512M to 1G as long as CONFIG_RANDOMIZE_BASE is enabled.

So in this patch, change to determine the size of kernel image mapping
area at runtime. Though KASLR code compiled in, if "nokaslr" specified,
still make kernel mapping size be 512M.

Signed-off-by: Baoquan He <bhe@xxxxxxxxxx>
Acked-by: Kees Cook <keescook@xxxxxxxxxxxx>
---
arch/x86/boot/compressed/kaslr.c | 2 +-
arch/x86/include/asm/kaslr.h | 1 +
arch/x86/include/asm/page_64_types.h | 6 +-----
arch/x86/kernel/head64.c | 11 ++++++-----
arch/x86/mm/dump_pagetables.c | 3 ++-
5 files changed, 11 insertions(+), 12 deletions(-)

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 6ded03b..823f294 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -445,7 +445,7 @@ void choose_random_location(unsigned long input,
}

#ifdef CONFIG_X86_64
- _kernel_mapping_size = KERNEL_MAPPING_SIZE;
+ _kernel_mapping_size = KERNEL_MAPPING_SIZE_EXT;
#endif

boot_params->hdr.loadflags |= KASLR_FLAG;
diff --git a/arch/x86/include/asm/kaslr.h b/arch/x86/include/asm/kaslr.h
index 1052a79..093935d 100644
--- a/arch/x86/include/asm/kaslr.h
+++ b/arch/x86/include/asm/kaslr.h
@@ -2,6 +2,7 @@
#define _ASM_KASLR_H_

unsigned long kaslr_get_random_long(const char *purpose);
+extern unsigned long kernel_mapping_size;

#ifdef CONFIG_RANDOMIZE_MEMORY
extern unsigned long page_offset_base;
diff --git a/arch/x86/include/asm/page_64_types.h b/arch/x86/include/asm/page_64_types.h
index 20a5a9b..b8e79d7 100644
--- a/arch/x86/include/asm/page_64_types.h
+++ b/arch/x86/include/asm/page_64_types.h
@@ -64,10 +64,6 @@
* kernel page table mapping, reducing the size of the modules area.
*/
#define KERNEL_MAPPING_SIZE_EXT (1024 * 1024 * 1024)
-#if defined(CONFIG_RANDOMIZE_BASE)
-#define KERNEL_MAPPING_SIZE KERNEL_MAPPING_SIZE_EXT
-#else
-#define KERNEL_MAPPING_SIZE KERNEL_IMAGE_SIZE
-#endif
+#define KERNEL_MAPPING_SIZE kernel_mapping_size

#endif /* _ASM_X86_PAGE_64_DEFS_H */
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index 54a2372..46d2bd2 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -28,6 +28,7 @@
#include <asm/bootparam_utils.h>
#include <asm/microcode.h>
#include <asm/kasan.h>
+#include <asm/cmdline.h>

/*
* Manage page tables very early on.
@@ -36,6 +37,7 @@ extern pgd_t early_level4_pgt[PTRS_PER_PGD];
extern pmd_t early_dynamic_pgts[EARLY_DYNAMIC_PAGE_TABLES][PTRS_PER_PMD];
static unsigned int __initdata next_early_pgt = 2;
pmdval_t early_pmd_flags = __PAGE_KERNEL_LARGE & ~(_PAGE_GLOBAL | _PAGE_NX);
+unsigned long kernel_mapping_size = KERNEL_IMAGE_SIZE;

/* Wipe all early page tables except for the kernel symbol map */
static void __init reset_early_page_tables(void)
@@ -138,12 +140,7 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)
* Build-time sanity checks on the kernel image and module
* area mappings. (these are purely build-time and produce no code)
*/
- BUILD_BUG_ON(MODULES_VADDR < __START_KERNEL_map);
- BUILD_BUG_ON(MODULES_VADDR - __START_KERNEL_map < KERNEL_IMAGE_SIZE);
- BUILD_BUG_ON(MODULES_LEN + KERNEL_IMAGE_SIZE > 2*PUD_SIZE);
BUILD_BUG_ON((__START_KERNEL_map & ~PMD_MASK) != 0);
- BUILD_BUG_ON((MODULES_VADDR & ~PMD_MASK) != 0);
- BUILD_BUG_ON(!(MODULES_VADDR > __START_KERNEL));
BUILD_BUG_ON(!(((MODULES_END - 1) & PGDIR_MASK) ==
(__START_KERNEL & PGDIR_MASK)));
BUILD_BUG_ON(__fix_to_virt(__end_of_fixed_addresses) <= MODULES_END);
@@ -165,6 +162,10 @@ asmlinkage __visible void __init x86_64_start_kernel(char * real_mode_data)

copy_bootdata(__va(real_mode_data));

+ if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) &&
+ !cmdline_find_option_bool(boot_command_line, "nokaslr"))
+ kernel_mapping_size = KERNEL_MAPPING_SIZE_EXT;
+
/*
* Load microcode early on BSP.
*/
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index ea9c49a..412c3f5 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -82,7 +82,7 @@ static struct addr_marker address_markers[] = {
{ EFI_VA_END, "EFI Runtime Services" },
# endif
{ __START_KERNEL_map, "High Kernel Mapping" },
- { MODULES_VADDR, "Modules" },
+ { 0/*MODULES_VADDR*/, "Modules" },
{ MODULES_END, "End Modules" },
#else
{ PAGE_OFFSET, "Kernel Mapping" },
@@ -442,6 +442,7 @@ static int __init pt_dump_init(void)
address_markers[LOW_KERNEL_NR].start_address = PAGE_OFFSET;
address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
address_markers[VMEMMAP_START_NR].start_address = VMEMMAP_START;
+ address_markers[MODULES_VADDR_NR].start_address = MODULES_VADDR;
#endif
#ifdef CONFIG_X86_32
address_markers[VMALLOC_START_NR].start_address = VMALLOC_START;
--
2.5.5