From patchwork Wed Sep 12 22:55:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Edgecombe, Rick P" X-Patchwork-Id: 10598433 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0E06614F9 for ; Wed, 12 Sep 2018 22:55:44 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id F03C02AC76 for ; Wed, 12 Sep 2018 22:55:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id E3A2F2AC7E; Wed, 12 Sep 2018 22:55:43 +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=-2.9 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.1 Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 34DAC2AC76 for ; Wed, 12 Sep 2018 22:55:43 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id 8959C8E0002; Wed, 12 Sep 2018 18:55:39 -0400 (EDT) Delivered-To: linux-mm-outgoing@kvack.org Received: by kanga.kvack.org (Postfix, from userid 40) id 845388E0001; Wed, 12 Sep 2018 18:55:39 -0400 (EDT) X-Original-To: int-list-linux-mm@kvack.org X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 676A98E0005; Wed, 12 Sep 2018 18:55:39 -0400 (EDT) X-Original-To: linux-mm@kvack.org X-Delivered-To: linux-mm@kvack.org Received: from mail-pl1-f200.google.com (mail-pl1-f200.google.com [209.85.214.200]) by kanga.kvack.org (Postfix) with ESMTP id 164518E0002 for ; Wed, 12 Sep 2018 18:55:39 -0400 (EDT) Received: by mail-pl1-f200.google.com with SMTP id d10-v6so1629950pll.22 for ; Wed, 12 Sep 2018 15:55:39 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-original-authentication-results:x-gm-message-state:from:to:cc :subject:date:message-id:in-reply-to:references; bh=QqwWDnXJn8z6+qpoq1dVwrfhWZlisBIiIIAv83FSdxI=; b=HA9jCu63o0dM4yX72mvDfv16KQ+l9XhFn41kTwTONqYyBkPIuOrthSLLpAXqifp1zh Aa3Q2md4mijD/13Ro0dqlQZ8U3xnZ3zlAhg0t1tpJ5Sti8UN0KCOq6CQuhl5pllJ1V9s skECgGPTbpmSMesDNrbavKheCIes+ygjbHHy4Q3kZBWdgGHf7AbacQHEwq07nB5a/3+V UCAo0pegRJWi9KNU9Owa9a83T7Qt2wA/S+UqZHOo5gpQsQ6ce6D31u3264QhIUcJtOxT 9GMeyZqAIc8iaZD2Od+1iXaLrJ6ua61Pelb1Bc4atkNlGC+1ejYKTKOWhB1ok2ng9+kR MUXQ== X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of rick.p.edgecombe@intel.com designates 134.134.136.65 as permitted sender) smtp.mailfrom=rick.p.edgecombe@intel.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com X-Gm-Message-State: APzg51B4UMdvO/oJVzT5e/pgWcQkNYBYOfBfnBAtxclVA20wUcX+DvAE efevUsSWKYkQ/sAyMgfEhcyVR1ieFQfIwGXEaOJ7Epw0iRC22E0bAVnQv6OA7+6TWz3e3gu2RSH L0GnF9C3+px+7pSBGdRL6JmBkP2arY/jM1fzmA/J/HQ/Fs8o35KE/z32RwhCls0+jgQ== X-Received: by 2002:a17:902:9a8a:: with SMTP id w10-v6mr4388572plp.14.1536792938737; Wed, 12 Sep 2018 15:55:38 -0700 (PDT) X-Google-Smtp-Source: ANB0VdZ1vQsITSKX/PB7vDdi6O1xpfeF1dVvX1BmXl8YQ++ggZUesa5WQwzYtgiFiItTkh3/c1iJ X-Received: by 2002:a17:902:9a8a:: with SMTP id w10-v6mr4388546plp.14.1536792937504; Wed, 12 Sep 2018 15:55:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1536792937; cv=none; d=google.com; s=arc-20160816; b=Xz5MiAQAg2Cs+pgqyGcZwXX6v9SOGBMGlJYQP/Zu6U3usRtB7GbMTwGx+gxKLAQ42K eSs+0Ky4lnRKVjvOv9GGjvXXHIH4CcoLVFrVVAVO9sGIBilB/UDwkXFM4MwTU4KbKHH0 g9EhapOMnpfskkLLywB8/G/wymXULtvPh2Ox/DnZCAXUq5u6WP5ZDlgeg0eHYaiR3hO1 GVMzP4HCsf3RdIVgTtMpNvhkqEJuQsdQ1vORvNY4XvAk3tvV9UiBuEYCf8LrDfodmYl6 ovlOlCtY9yzgkgYPta4FE62nfz8SJPLEmAabQTmLecMCiZg+uPMSiYzTxjxdQYAN4EhZ YE0w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=references:in-reply-to:message-id:date:subject:cc:to:from; bh=QqwWDnXJn8z6+qpoq1dVwrfhWZlisBIiIIAv83FSdxI=; b=xmWMOjefskyl3Cd0F0QsXxinS/GEa+NdsLiNbZ4rmUeZmfn4z2la5OR7+aE7xdyJrV cJxKmf04AGIIE+fitiuLQEXMPZ8VAIYDaP3+bqJZv5z81BOQfd9GtLtpMYFnjx2E6dY/ iJ5Nh2NIxVr1ag3RdTcZq6YPzB9aWNyNxj2LmuLPR+cFINniirLE8fXCo43BzSmG0q8J eSp253VG6YbuedcSveZJvtv5d9x5be1GTpuQlNLm6ywuS2ZD2VBFdzSXSFn4ay5K1kki 0LWKLw6IUYKzKV7zq+2ZBEOt9WhMXI2vlxz4/sLilF4I4i5EwmzWWzRJDjYs5XLMPKPp ugSA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of rick.p.edgecombe@intel.com designates 134.134.136.65 as permitted sender) smtp.mailfrom=rick.p.edgecombe@intel.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: from mga03.intel.com (mga03.intel.com. [134.134.136.65]) by mx.google.com with ESMTPS id n5-v6si2470946pgf.529.2018.09.12.15.55.37 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 12 Sep 2018 15:55:37 -0700 (PDT) Received-SPF: pass (google.com: domain of rick.p.edgecombe@intel.com designates 134.134.136.65 as permitted sender) client-ip=134.134.136.65; Authentication-Results: mx.google.com; spf=pass (google.com: domain of rick.p.edgecombe@intel.com designates 134.134.136.65 as permitted sender) smtp.mailfrom=rick.p.edgecombe@intel.com; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=intel.com X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga007.fm.intel.com ([10.253.24.52]) by orsmga103.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 12 Sep 2018 15:55:36 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.53,366,1531810800"; d="scan'208";a="69545859" Received: from rpedgeco-desk5.jf.intel.com ([10.54.75.168]) by fmsmga007.fm.intel.com with ESMTP; 12 Sep 2018 15:55:22 -0700 From: Rick Edgecombe To: tglx@linutronix.de, mingo@redhat.com, hpa@zytor.com, x86@kernel.org, linux-kernel@vger.kernel.org, linux-mm@kvack.org, kernel-hardening@lists.openwall.com, daniel@iogearbox.net, jannh@google.com, keescook@chromium.org, alexei.starovoitov@gmail.com Cc: kristen@linux.intel.com, dave.hansen@intel.com, arjan@linux.intel.com, Rick Edgecombe Subject: [PATCH v5 2/4] x86/modules: Increase randomization for modules Date: Wed, 12 Sep 2018 15:55:38 -0700 Message-Id: <1536792940-8294-3-git-send-email-rick.p.edgecombe@intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1536792940-8294-1-git-send-email-rick.p.edgecombe@intel.com> References: <1536792940-8294-1-git-send-email-rick.p.edgecombe@intel.com> X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: X-Virus-Scanned: ClamAV using ClamSMTP This changes the behavior of the KASLR logic for allocating memory for the text sections of loadable modules. It randomizes the location of each module text section with about 17 bits of entropy in typical use. This is enabled on X86_64 only. For 32 bit, the behavior is unchanged. It refactors existing code around module randomization somewhat. There are now three different behaviors for x86 module_alloc depending on config. RANDOMIZE_BASE=n, and RANDOMIZE_BASE=y ARCH=x86_64, and RANDOMIZE_BASE=y ARCH=i386. The refactor of the existing code is to try to clearly show what those behaviors are without having three separate versions or threading the behaviors in a bunch of little spots. The reason it is not enabled on 32 bit yet is because the module space is much smaller and simulations haven't been run to see how it performs. The new algorithm breaks the module space in two, a random area and a backup area. It first tries to allocate at a number of randomly located starting pages inside the random section without purging any lazy free vmap areas and triggering the associated TLB flush. If this fails, it will try again a number of times allowing for purges if needed. It also saves any position that could have succeeded if it was allowed to purge, which doubles the chances of finding a spot that would fit. Finally if those both fail to find a position it will allocate in the backup area. The backup area base will be offset in the same way as the current algorithm does for the base area, 1024 possible locations. Signed-off-by: Rick Edgecombe --- arch/x86/include/asm/pgtable_64_types.h | 7 ++ arch/x86/kernel/module.c | 165 +++++++++++++++++++++++++++----- 2 files changed, 149 insertions(+), 23 deletions(-) diff --git a/arch/x86/include/asm/pgtable_64_types.h b/arch/x86/include/asm/pgtable_64_types.h index 04edd2d..5e26369 100644 --- a/arch/x86/include/asm/pgtable_64_types.h +++ b/arch/x86/include/asm/pgtable_64_types.h @@ -143,6 +143,13 @@ extern unsigned int ptrs_per_p4d; #define MODULES_END _AC(0xffffffffff000000, UL) #define MODULES_LEN (MODULES_END - MODULES_VADDR) +/* + * Dedicate the first part of the module space to a randomized area when KASLR + * is in use. Leave the remaining part for a fallback if we are unable to + * allocate in the random area. + */ +#define MODULES_RAND_LEN PAGE_ALIGN((MODULES_LEN/3)*2) + #define ESPFIX_PGD_ENTRY _AC(-2, UL) #define ESPFIX_BASE_ADDR (ESPFIX_PGD_ENTRY << P4D_SHIFT) diff --git a/arch/x86/kernel/module.c b/arch/x86/kernel/module.c index f58336a..d50a0a0 100644 --- a/arch/x86/kernel/module.c +++ b/arch/x86/kernel/module.c @@ -48,34 +48,151 @@ do { \ } while (0) #endif -#ifdef CONFIG_RANDOMIZE_BASE +#if defined(CONFIG_X86_64) && defined(CONFIG_RANDOMIZE_BASE) +static inline unsigned long get_modules_rand_len(void) +{ + return MODULES_RAND_LEN; +} +#else +static inline unsigned long get_modules_rand_len(void) +{ + BUILD_BUG(); + return 0; +} + +inline bool kaslr_enabled(void); +#endif + +static inline int kaslr_randomize_each_module(void) +{ + return IS_ENABLED(CONFIG_RANDOMIZE_BASE) + && IS_ENABLED(CONFIG_X86_64) + && kaslr_enabled(); +} + +static inline int kaslr_randomize_base(void) +{ + return IS_ENABLED(CONFIG_RANDOMIZE_BASE) + && !IS_ENABLED(CONFIG_X86_64) + && kaslr_enabled(); +} + static unsigned long module_load_offset; +static const unsigned long NR_NO_PURGE = 5000; +static const unsigned long NR_TRY_PURGE = 5000; /* Mutex protects the module_load_offset. */ static DEFINE_MUTEX(module_kaslr_mutex); static unsigned long int get_module_load_offset(void) { - if (kaslr_enabled()) { - mutex_lock(&module_kaslr_mutex); - /* - * Calculate the module_load_offset the first time this - * code is called. Once calculated it stays the same until - * reboot. - */ - if (module_load_offset == 0) - module_load_offset = - (get_random_int() % 1024 + 1) * PAGE_SIZE; - mutex_unlock(&module_kaslr_mutex); - } + mutex_lock(&module_kaslr_mutex); + /* + * Calculate the module_load_offset the first time this + * code is called. Once calculated it stays the same until + * reboot. + */ + if (module_load_offset == 0) + module_load_offset = (get_random_int() % 1024 + 1) * PAGE_SIZE; + mutex_unlock(&module_kaslr_mutex); + return module_load_offset; } -#else -static unsigned long int get_module_load_offset(void) + +static unsigned long get_module_vmalloc_start(void) { - return 0; + if (kaslr_randomize_each_module()) + return MODULES_VADDR + get_modules_rand_len() + + get_module_load_offset(); + else if (kaslr_randomize_base()) + return MODULES_VADDR + get_module_load_offset(); + + return MODULES_VADDR; +} + +static void *try_module_alloc(unsigned long addr, unsigned long size, + int try_purge) +{ + const unsigned long vm_flags = 0; + + return __vmalloc_node_try_addr(addr, size, GFP_KERNEL, PAGE_KERNEL_EXEC, + vm_flags, NUMA_NO_NODE, try_purge, + __builtin_return_address(0)); +} + +/* + * Find a random address to try that won't obviously not fit. Random areas are + * allowed to overflow into the backup area + */ +static unsigned long get_rand_module_addr(unsigned long size) +{ + unsigned long nr_max_pos = (MODULES_LEN - size) / MODULE_ALIGN + 1; + unsigned long nr_rnd_pos = get_modules_rand_len() / MODULE_ALIGN; + unsigned long nr_pos = min(nr_max_pos, nr_rnd_pos); + + unsigned long module_position_nr = get_random_long() % nr_pos; + unsigned long offset = module_position_nr * MODULE_ALIGN; + + return MODULES_VADDR + offset; +} + +/* + * Try to allocate in the random area. First 5000 times without purging, then + * 5000 times with purging. If these fail, return NULL. + */ +static void *try_module_randomize_each(unsigned long size) +{ + void *p = NULL; + unsigned int i; + unsigned long last_lazy_free_blocked = 0; + + /* This will have a guard page */ + unsigned long va_size = PAGE_ALIGN(size) + PAGE_SIZE; + + if (!kaslr_randomize_each_module()) + return NULL; + + /* Make sure there is at least one address that might fit. */ + if (va_size < PAGE_ALIGN(size) || va_size > MODULES_LEN) + return NULL; + + /* Try to find a spot that doesn't need a lazy purge */ + for (i = 0; i < NR_NO_PURGE; i++) { + unsigned long addr = get_rand_module_addr(va_size); + + /* First try to avoid having to purge */ + p = try_module_alloc(addr, size, 0); + + /* + * Save the last value that was blocked by a + * lazy purge area. + */ + if (IS_ERR(p) && PTR_ERR(p) == -EUCLEAN) + last_lazy_free_blocked = addr; + else if (!IS_ERR(p)) + return p; + } + + /* Try the most recent spot that could be used after a lazy purge */ + if (last_lazy_free_blocked) { + p = try_module_alloc(last_lazy_free_blocked, size, 1); + + if (!IS_ERR(p)) + return p; + } + + /* Look for more spots and allow lazy purges */ + for (i = 0; i < NR_TRY_PURGE; i++) { + unsigned long addr = get_rand_module_addr(va_size); + + /* Give up and allow for purges */ + p = try_module_alloc(addr, size, 1); + + if (!IS_ERR(p)) + return p; + } + return NULL; } -#endif void *module_alloc(unsigned long size) { @@ -84,16 +201,18 @@ void *module_alloc(unsigned long size) if (PAGE_ALIGN(size) > MODULES_LEN) return NULL; - p = __vmalloc_node_range(size, MODULE_ALIGN, - MODULES_VADDR + get_module_load_offset(), - MODULES_END, GFP_KERNEL, - PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE, - __builtin_return_address(0)); + p = try_module_randomize_each(size); + + if (!p) + p = __vmalloc_node_range(size, MODULE_ALIGN, + get_module_vmalloc_start(), MODULES_END, + GFP_KERNEL, PAGE_KERNEL_EXEC, 0, + NUMA_NO_NODE, __builtin_return_address(0)); + if (p && (kasan_module_alloc(p, size) < 0)) { vfree(p); return NULL; } - return p; }