Skip to content

Commit

Permalink
x86/boot: Fix KASLR and memmap= collision
Browse files Browse the repository at this point in the history
CONFIG_RANDOMIZE_BASE=y relocates the kernel to a random base address.

However it does not take into account the memmap= parameter passed in from
the kernel command line. This results in the kernel sometimes being put in
the middle of memmap.

Teach KASLR to not insert the kernel in memmap defined regions. We support
up to 4 memmap regions: any additional regions will cause KASLR to disable.

The mem_avoid set has been augmented to add up to 4 unusable regions of
memmaps provided by the user to exclude those regions from the set of valid
address range to insert the uncompressed kernel image.

The nn@ss ranges will be skipped by the mem_avoid set since it indicates
that memory is useable.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Kees Cook <keescook@chromium.org>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: dan.j.williams@intel.com
Cc: david@fromorbit.com
Cc: linux-nvdimm@lists.01.org
Link: http://lkml.kernel.org/r/148417664156.131935.2248592164852799738.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Dave Jiang authored and Ingo Molnar committed Jan 25, 2017
1 parent c19a5f3 commit f284424
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 3 deletions.
1 change: 1 addition & 0 deletions arch/x86/boot/boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,7 @@ size_t strnlen(const char *s, size_t maxlen);
unsigned int atou(const char *s);
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base);
size_t strlen(const char *s);
char *strchr(const char *s, int c);

/* tty.c */
void puts(const char *);
Expand Down
140 changes: 137 additions & 3 deletions arch/x86/boot/compressed/kaslr.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
*/
#include "misc.h"
#include "error.h"
#include "../boot.h"

#include <generated/compile.h>
#include <linux/module.h>
Expand Down Expand Up @@ -52,15 +53,22 @@ static unsigned long get_boot_seed(void)
#include "../../lib/kaslr.c"

struct mem_vector {
unsigned long start;
unsigned long size;
unsigned long long start;
unsigned long long size;
};

/* Only supporting at most 4 unusable memmap regions with kaslr */
#define MAX_MEMMAP_REGIONS 4

static bool memmap_too_large;

enum mem_avoid_index {
MEM_AVOID_ZO_RANGE = 0,
MEM_AVOID_INITRD,
MEM_AVOID_CMDLINE,
MEM_AVOID_BOOTPARAMS,
MEM_AVOID_MEMMAP_BEGIN,
MEM_AVOID_MEMMAP_END = MEM_AVOID_MEMMAP_BEGIN + MAX_MEMMAP_REGIONS - 1,
MEM_AVOID_MAX,
};

Expand All @@ -77,6 +85,123 @@ static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two)
return true;
}

/**
* _memparse - Parse a string with mem suffixes into a number
* @ptr: Where parse begins
* @retptr: (output) Optional pointer to next char after parse completes
*
* Parses a string into a number. The number stored at @ptr is
* potentially suffixed with K, M, G, T, P, E.
*/
static unsigned long long _memparse(const char *ptr, char **retptr)
{
char *endptr; /* Local pointer to end of parsed string */

unsigned long long ret = simple_strtoull(ptr, &endptr, 0);

switch (*endptr) {
case 'E':
case 'e':
ret <<= 10;
case 'P':
case 'p':
ret <<= 10;
case 'T':
case 't':
ret <<= 10;
case 'G':
case 'g':
ret <<= 10;
case 'M':
case 'm':
ret <<= 10;
case 'K':
case 'k':
ret <<= 10;
endptr++;
default:
break;
}

if (retptr)
*retptr = endptr;

return ret;
}

static int
parse_memmap(char *p, unsigned long long *start, unsigned long long *size)
{
char *oldp;

if (!p)
return -EINVAL;

/* We don't care about this option here */
if (!strncmp(p, "exactmap", 8))
return -EINVAL;

oldp = p;
*size = _memparse(p, &p);
if (p == oldp)
return -EINVAL;

switch (*p) {
case '@':
/* Skip this region, usable */
*start = 0;
*size = 0;
return 0;
case '#':
case '$':
case '!':
*start = _memparse(p + 1, &p);
return 0;
}

return -EINVAL;
}

static void mem_avoid_memmap(void)
{
char arg[128];
int rc;
int i;
char *str;

/* See if we have any memmap areas */
rc = cmdline_find_option("memmap", arg, sizeof(arg));
if (rc <= 0)
return;

i = 0;
str = arg;
while (str && (i < MAX_MEMMAP_REGIONS)) {
int rc;
unsigned long long start, size;
char *k = strchr(str, ',');

if (k)
*k++ = 0;

rc = parse_memmap(str, &start, &size);
if (rc < 0)
break;
str = k;
/* A usable region that should not be skipped */
if (size == 0)
continue;

mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].start = start;
mem_avoid[MEM_AVOID_MEMMAP_BEGIN + i].size = size;
i++;
}

/* More than 4 memmaps, fail kaslr */
if ((i >= MAX_MEMMAP_REGIONS) && str)
memmap_too_large = true;
}

/*
* In theory, KASLR can put the kernel anywhere in the range of [16M, 64T).
* The mem_avoid array is used to store the ranges that need to be avoided
Expand Down Expand Up @@ -197,6 +322,9 @@ static void mem_avoid_init(unsigned long input, unsigned long input_size,

/* We don't need to set a mapping for setup_data. */

/* Mark the memmap regions we need to avoid */
mem_avoid_memmap();

#ifdef CONFIG_X86_VERBOSE_BOOTUP
/* Make sure video RAM can be used. */
add_identity_map(0, PMD_SIZE);
Expand Down Expand Up @@ -379,6 +507,12 @@ static unsigned long find_random_phys_addr(unsigned long minimum,
int i;
unsigned long addr;

/* Check if we had too many memmaps. */
if (memmap_too_large) {
debug_putstr("Aborted e820 scan (more than 4 memmap= args)!\n");
return 0;
}

/* Make sure minimum is aligned. */
minimum = ALIGN(minimum, CONFIG_PHYSICAL_ALIGN);

Expand Down Expand Up @@ -456,7 +590,7 @@ void choose_random_location(unsigned long input,
/* Walk e820 and find a random address. */
random_addr = find_random_phys_addr(min_addr, output_size);
if (!random_addr) {
warn("KASLR disabled: could not find suitable E820 region!");
warn("Physical KASLR disabled: no suitable memory region!");
} else {
/* Update the new physical address location. */
if (*output != random_addr) {
Expand Down
13 changes: 13 additions & 0 deletions arch/x86/boot/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,16 @@ char *strstr(const char *s1, const char *s2)
}
return NULL;
}

/**
* strchr - Find the first occurrence of the character c in the string s.
* @s: the string to be searched
* @c: the character to search for
*/
char *strchr(const char *s, int c)
{
while (*s != (char)c)
if (*s++ == '\0')
return NULL;
return (char *)s;
}

0 comments on commit f284424

Please sign in to comment.