On 01/03/2017 07:37 PM, Baoquan He wrote: > Hi Dave, > > I have several concerns, please see the inline comments. > > On 01/03/17 at 01:48pm, Dave Jiang wrote: >> CONFIG_RANDOMIZE_BASE relocates the kernel to a random base address. >> However it does not take into account the memmap= parameter passed in from >> the kernel cmdline. This results in the kernel sometimes being put in >> the middle of the user memmap. Teaching kaslr to not insert the kernel in > ~~~~~~~~~~~ > It could be better to be replaced with "user-defined physical RAM map" > or memmap directly because memmap is meaning user-defined physical RAM > map. Please check the boot info printing about them. I was confused at > first glance.
Will update >> memmap defined regions. We will 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 regions of memmaps provided by the user > ~~~ > 4 un-usable memmap region need be cared, usable memory is not > concerned Will update . >> 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 memory useable. >> >> Signed-off-by: Dave Jiang <dave.ji...@intel.com> >> --- >> arch/x86/boot/boot.h | 3 + >> arch/x86/boot/compressed/kaslr.c | 131 >> ++++++++++++++++++++++++++++++++++++++ >> arch/x86/boot/string.c | 38 +++++++++++ >> 3 files changed, 172 insertions(+) >> >> diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h >> index e5612f3..59c2075 100644 >> --- a/arch/x86/boot/boot.h >> +++ b/arch/x86/boot/boot.h >> @@ -332,7 +332,10 @@ int strncmp(const char *cs, const char *ct, size_t >> count); >> 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); >> +unsigned long simple_strtoul(const char *cp, char **endp, unsigned int >> base); >> +long simple_strtol(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 *); >> diff --git a/arch/x86/boot/compressed/kaslr.c >> b/arch/x86/boot/compressed/kaslr.c >> index a66854d..f5a1c8d 100644 >> --- a/arch/x86/boot/compressed/kaslr.c >> +++ b/arch/x86/boot/compressed/kaslr.c >> @@ -11,6 +11,7 @@ >> */ >> #include "misc.h" >> #include "error.h" >> +#include "../boot.h" >> >> #include <generated/compile.h> >> #include <linux/module.h> >> @@ -61,9 +62,16 @@ enum mem_avoid_index { >> MEM_AVOID_INITRD, >> MEM_AVOID_CMDLINE, >> MEM_AVOID_BOOTPARAMS, >> + MEM_AVOID_MEMMAP1, >> + MEM_AVOID_MEMMAP2, >> + MEM_AVOID_MEMMAP3, >> + MEM_AVOID_MEMMAP4, > > This looks not good. Could it be done like fixed_addresses? > Something like: > > MEM_AVOID_MEMMAP_BEGIN, > MEM_AVOID_MEMMAP_BEGIN + MEM_AVOID_MAX -1, > > Please point it out to me if there's some existing code in kernel like > your way, I can also accept it. I think you mean: MEM_AVOID_MEMMAP_BEGIN + MAX_MEMMAP_REGIONS - 1 I will change > >> MEM_AVOID_MAX, >> }; >> >> +/* only supporting at most 4 memmap regions with kaslr */ > And here, "Only supporting at most 4 un-usable memmap regions with kaslr"? > Surely this is based on if you will ignore the usable memory and do not > store it as 0. And also the log need be changed too accordingly. >> +#define MAX_MEMMAP_REGIONS 4 >> + >> static struct mem_vector mem_avoid[MEM_AVOID_MAX]; >> >> static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two) >> @@ -77,6 +85,121 @@ 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; > How about direclty return if nn@ss? Seems no need to waste one mem avoid > region slot. In fact even amount of usable memory regions are provided to > 100, it won't impact that you want to specify a reserve memmap region if > you skip it direclty. Personal opinion. We are not wasting the slot. If you look mem_avoid_memmap() where I call the function, it will skip with a continue if size == 0 without incrementing the 'i' counter. That will skip all the nn@ss regions without counting against the max avoid mapping. >> + case '#': >> + case '$': >> + case '!': >> + *start = _memparse(p + 1, &p); >> + return 0; >> + } >> + >> + return -EINVAL; >> +} >> + >> +static int mem_avoid_memmap(void) >> +{ >> + char arg[128]; >> + int rc = 0; >> + >> + /* see if we have any memmap areas */ >> + if (cmdline_find_option("memmap", arg, sizeof(arg)) > 0) { >> + int i = 0; >> + char *str = arg; >> + >> + while (str && (i < MAX_MEMMAP_REGIONS)) { >> + 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_MEMMAP1 + i].start = start; >> + mem_avoid[MEM_AVOID_MEMMAP1 + i].size = size; >> + i++; >> + } >> + >> + /* more than 4 memmaps, fail kaslr */ >> + if ((i >= MAX_MEMMAP_REGIONS) && str) >> + rc = -EINVAL; >> + } >> + >> + return rc; >> +} >> + >> /* >> * 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 >> @@ -429,6 +552,7 @@ void choose_random_location(unsigned long input, >> unsigned long *virt_addr) >> { >> unsigned long random_addr, min_addr; >> + int rc; >> >> /* By default, keep output position unchanged. */ >> *virt_addr = *output; >> @@ -438,6 +562,13 @@ void choose_random_location(unsigned long input, >> return; >> } >> >> + /* Mark the memmap regions we need to avoid */ >> + rc = mem_avoid_memmap(); >> + if (rc < 0) { > Or write it like below, the rc is not needed. Personal coding style, > just talk about it. > if (mem_avoid_memmap()) { > warn("KASLR disabled: memmap exceeds limit of 4, giving up."); > return; > } Doesn't matter to me. I'll update as you suggest. >> + >> boot_params->hdr.loadflags |= KASLR_FLAG; >> >> /* Prepare to add new identity pagetables on demand. */ >> diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c >> index cc3bd58..0464aaa 100644 >> --- a/arch/x86/boot/string.c >> +++ b/arch/x86/boot/string.c >> @@ -122,6 +122,31 @@ unsigned long long simple_strtoull(const char *cp, char >> **endp, unsigned int bas >> } >> >> /** >> + * simple_strtoul - convert a string to an unsigned long >> + * @cp: The start of the string >> + * @endp: A pointer to the end of the parsed string will be placed here >> + * @base: The number base to use >> + */ >> +unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base) >> +{ >> + return simple_strtoull(cp, endp, base); >> +} >> + >> +/** >> + * simple_strtol - convert a string to a signed long >> + * @cp: The start of the string >> + * @endp: A pointer to the end of the parsed string will be placed here >> + * @base: The number base to use >> + */ >> +long simple_strtol(const char *cp, char **endp, unsigned int base) >> +{ >> + if (*cp == '-') >> + return -simple_strtoul(cp + 1, endp, base); >> + >> + return simple_strtoul(cp, endp, base); >> +} >> + >> +/** >> * strlen - Find the length of a string >> * @s: The string to be sized >> */ >> @@ -155,3 +180,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; >> +} >>