Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755429Ab3JCUxx (ORCPT ); Thu, 3 Oct 2013 16:53:53 -0400 Received: from smtp.outflux.net ([198.145.64.163]:52227 "EHLO smtp.outflux.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754587Ab3JCUxu (ORCPT ); Thu, 3 Oct 2013 16:53:50 -0400 From: Kees Cook To: linux-kernel@vger.kernel.org Cc: x86@kernel.org, kernel-hardening@lists.openwall.com, adurbin@google.com, Eric Northup , jln@google.com, wad@google.com, Mathias Krause , Zhang Yanfei , "H. Peter Anvin" , keescook@chromium.org Subject: [PATCH 4/7] x86, kaslr: select random base offset Date: Thu, 3 Oct 2013 13:53:22 -0700 Message-Id: <1380833605-26313-5-git-send-email-keescook@chromium.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1380833605-26313-1-git-send-email-keescook@chromium.org> References: <1380833605-26313-1-git-send-email-keescook@chromium.org> X-HELO: www.outflux.net Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 5916 Lines: 204 Select a random location when CONFIG_RANDOMIZE_BASE is used, bounded by CONFIG_RANDOMIZE_BASE_MAX_OFFSET. Sources of randomness currently include RDRAND, RDTSC, or the i8254. This moves the pre-alternatives inline rdrand function into the header so both pieces of code can use it. Availability of RDRAND is then controlled by CONFIG_ARCH_RANDOM, if someone wants to disable it even for kASLR. Signed-off-by: Kees Cook --- v7: - move rdrand_long into header to avoid code duplication; HPA. v3: - fall back to reading the i8254 when no TSC; HPA. v2: - use rdtscl from msr.h; Mathias Krause. --- arch/x86/boot/compressed/aslr.c | 70 ++++++++++++++++++++++++++++++++++++- arch/x86/boot/compressed/misc.h | 2 ++ arch/x86/include/asm/archrandom.h | 21 +++++++++++ arch/x86/kernel/cpu/rdrand.c | 14 -------- 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c index ed7e9f0..5bb7c63 100644 --- a/arch/x86/boot/compressed/aslr.c +++ b/arch/x86/boot/compressed/aslr.c @@ -1,6 +1,59 @@ #include "misc.h" #ifdef CONFIG_RANDOMIZE_BASE +#include +#include + +#define I8254_PORT_CONTROL 0x43 +#define I8254_PORT_COUNTER0 0x40 +#define I8254_CMD_READBACK 0xC0 +#define I8254_SELECT_COUNTER0 0x02 +#define I8254_STATUS_NOTREADY 0x40 +static inline u16 i8254(void) +{ + u16 status, timer; + + do { + outb(I8254_PORT_CONTROL, + I8254_CMD_READBACK | I8254_SELECT_COUNTER0); + status = inb(I8254_PORT_COUNTER0); + timer = inb(I8254_PORT_COUNTER0); + timer |= inb(I8254_PORT_COUNTER0) << 8; + } while (status & I8254_STATUS_NOTREADY); + + return timer; +} + +static unsigned long get_random_long(void) +{ + unsigned long random; + + if (has_cpuflag(X86_FEATURE_RDRAND)) { + debug_putstr("KASLR using RDRAND...\n"); + if (rdrand_long(&random)) + return random; + } + + if (has_cpuflag(X86_FEATURE_TSC)) { + uint32_t raw; + + debug_putstr("KASLR using RDTSC...\n"); + rdtscl(raw); + + /* Only use the low bits of rdtsc. */ + random = raw & 0xffff; + } else { + debug_putstr("KASLR using i8254...\n"); + random = i8254(); + } + + /* Extend timer bits poorly... */ + random |= (random << 16); +#ifdef CONFIG_X86_64 + random |= (random << 32); +#endif + return random; +} static unsigned long find_minimum_location(unsigned long input, unsigned long input_size, @@ -55,6 +108,7 @@ unsigned char *choose_kernel_location(unsigned char *input, unsigned long output_size) { unsigned long choice = (unsigned long)output; + unsigned long random; if (cmdline_find_option_bool("nokaslr")) { debug_putstr("KASLR disabled...\n"); @@ -64,8 +118,22 @@ unsigned char *choose_kernel_location(unsigned char *input, choice = find_minimum_location((unsigned long)input, input_size, (unsigned long)output, output_size); - /* XXX: choose random location. */ + /* XXX: Find an appropriate E820 hole, instead of adding offset. */ + random = get_random_long(); + + /* Clip off top of the range. */ + random &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1); + while (random + output_size > CONFIG_RANDOMIZE_BASE_MAX_OFFSET) + random >>= 1; + + /* Clip off bottom of range. */ + random &= ~(choice - 1); + + /* Always enforce the minimum. */ + if (random < choice) + goto out; + choice = random; out: return (unsigned char *)choice; } diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 42f71bb..24e3e56 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h @@ -60,6 +60,8 @@ unsigned char *choose_kernel_location(unsigned char *input, unsigned long input_size, unsigned char *output, unsigned long output_size); +/* cpuflags.c */ +bool has_cpuflag(int flag); #else static inline unsigned char *choose_kernel_location(unsigned char *input, diff --git a/arch/x86/include/asm/archrandom.h b/arch/x86/include/asm/archrandom.h index 0d9ec77..e6a9245 100644 --- a/arch/x86/include/asm/archrandom.h +++ b/arch/x86/include/asm/archrandom.h @@ -39,6 +39,20 @@ #ifdef CONFIG_ARCH_RANDOM +/* Instead of arch_get_random_long() when alternatives haven't run. */ +static inline int rdrand_long(unsigned long *v) +{ + int ok; + asm volatile("1: " RDRAND_LONG "\n\t" + "jc 2f\n\t" + "decl %0\n\t" + "jnz 1b\n\t" + "2:" + : "=r" (ok), "=a" (*v) + : "0" (RDRAND_RETRY_LOOPS)); + return ok; +} + #define GET_RANDOM(name, type, rdrand, nop) \ static inline int name(type *v) \ { \ @@ -68,6 +82,13 @@ GET_RANDOM(arch_get_random_int, unsigned int, RDRAND_INT, ASM_NOP3); #endif /* CONFIG_X86_64 */ +#else + +static inline int rdrand_long(unsigned long *v) +{ + return 0; +} + #endif /* CONFIG_ARCH_RANDOM */ extern void x86_init_rdrand(struct cpuinfo_x86 *c); diff --git a/arch/x86/kernel/cpu/rdrand.c b/arch/x86/kernel/cpu/rdrand.c index 88db010..384df51 100644 --- a/arch/x86/kernel/cpu/rdrand.c +++ b/arch/x86/kernel/cpu/rdrand.c @@ -31,20 +31,6 @@ static int __init x86_rdrand_setup(char *s) } __setup("nordrand", x86_rdrand_setup); -/* We can't use arch_get_random_long() here since alternatives haven't run */ -static inline int rdrand_long(unsigned long *v) -{ - int ok; - asm volatile("1: " RDRAND_LONG "\n\t" - "jc 2f\n\t" - "decl %0\n\t" - "jnz 1b\n\t" - "2:" - : "=r" (ok), "=a" (*v) - : "0" (RDRAND_RETRY_LOOPS)); - return ok; -} - /* * Force a reseed cycle; we are architecturally guaranteed a reseed * after no more than 512 128-bit chunks of random data. This also -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/