From patchwork Tue Dec 20 21:08:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Jiang X-Patchwork-Id: 9482231 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 6045660237 for ; Tue, 20 Dec 2016 21:08:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 53D3528391 for ; Tue, 20 Dec 2016 21:08:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 47E78283E0; Tue, 20 Dec 2016 21:08:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-1.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id C7A5028391 for ; Tue, 20 Dec 2016 21:08:15 +0000 (UTC) Received: from [127.0.0.1] (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 501C98216B; Tue, 20 Dec 2016 13:08:15 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 8144082124 for ; Tue, 20 Dec 2016 13:08:14 -0800 (PST) Received: from orsmga003.jf.intel.com ([10.7.209.27]) by fmsmga104.fm.intel.com with ESMTP; 20 Dec 2016 13:08:14 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.33,380,1477983600"; d="scan'208";a="914547066" Received: from djiang5-desk3.ch.intel.com ([143.182.137.38]) by orsmga003.jf.intel.com with ESMTP; 20 Dec 2016 13:08:13 -0800 Subject: [PATCH v3] x86: fix kaslr and memmap collision From: Dave Jiang To: tglx@linutronix.de, mingo@redhat.com, hpa@zytor.com Date: Tue, 20 Dec 2016 14:08:13 -0700 Message-ID: <148226809303.147314.941589993797181767.stgit@djiang5-desk3.ch.intel.com> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: x86@kernel.org, david@fromorbit.com, linux-kernel@vger.kernel.org, linux-nvdimm@lists.01.org Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Virus-Scanned: ClamAV using ClamSMTP 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 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 to exclude those regions from the set of valid address range to insert the uncompressed kernel image. Signed-off-by: Dave Jiang --- arch/x86/boot/boot.h | 3 + arch/x86/boot/compressed/kaslr.c | 124 ++++++++++++++++++++++++++++++++++++++ arch/x86/boot/string.c | 38 ++++++++++++ 3 files changed, 165 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..be2b414 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 #include @@ -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, MEM_AVOID_MAX, }; +/* only supporting at most 4 memmap regions with kaslr */ +#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,114 @@ 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 '@': + 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; + + 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 +545,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 +555,13 @@ void choose_random_location(unsigned long input, return; } + /* Mark the memmap regions we need to avoid */ + rc = mem_avoid_memmap(); + if (rc < 0) { + warn("KASLR disabled: memmap exceeds limit of 4, giving up."); + return; + } + 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; +}