diff options
author | Kees Cook <keescook@chromium.org> | 2013-10-10 20:18:14 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2013-10-13 06:12:07 -0400 |
commit | 8ab3820fd5b2896d66da7bb2a906bc382e63e7bc (patch) | |
tree | 50f6f4458f86eb66552c13bef81169e44d27dc28 | |
parent | dd78b97367bd575918204cc89107c1479d3fc1a7 (diff) |
x86, kaslr: Return location from decompress_kernel
This allows decompress_kernel to return a new location for the kernel to
be relocated to. Additionally, enforces CONFIG_PHYSICAL_START as the
minimum relocation position when building with CONFIG_RELOCATABLE.
With CONFIG_RANDOMIZE_BASE set, the choose_kernel_location routine
will select a new location to decompress the kernel, though here it is
presently a no-op. The kernel command line option "nokaslr" is introduced
to bypass these routines.
Signed-off-by: Kees Cook <keescook@chromium.org>
Link: http://lkml.kernel.org/r/1381450698-28710-3-git-send-email-keescook@chromium.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
-rw-r--r-- | Documentation/kernel-parameters.txt | 4 | ||||
-rw-r--r-- | arch/x86/Kconfig | 38 | ||||
-rw-r--r-- | arch/x86/boot/compressed/Makefile | 2 | ||||
-rw-r--r-- | arch/x86/boot/compressed/aslr.c | 23 | ||||
-rw-r--r-- | arch/x86/boot/compressed/cmdline.c | 2 | ||||
-rw-r--r-- | arch/x86/boot/compressed/head_32.S | 10 | ||||
-rw-r--r-- | arch/x86/boot/compressed/head_64.S | 16 | ||||
-rw-r--r-- | arch/x86/boot/compressed/misc.c | 8 | ||||
-rw-r--r-- | arch/x86/boot/compressed/misc.h | 27 |
9 files changed, 106 insertions, 24 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fcbb736d55fe..773fc4c077b4 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
@@ -1975,6 +1975,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. | |||
1975 | noapic [SMP,APIC] Tells the kernel to not make use of any | 1975 | noapic [SMP,APIC] Tells the kernel to not make use of any |
1976 | IOAPICs that may be present in the system. | 1976 | IOAPICs that may be present in the system. |
1977 | 1977 | ||
1978 | nokaslr [X86] | ||
1979 | Disable kernel base offset ASLR (Address Space | ||
1980 | Layout Randomization) if built into the kernel. | ||
1981 | |||
1978 | noautogroup Disable scheduler automatic task group creation. | 1982 | noautogroup Disable scheduler automatic task group creation. |
1979 | 1983 | ||
1980 | nobats [PPC] Do not use BATs for mapping kernel lowmem | 1984 | nobats [PPC] Do not use BATs for mapping kernel lowmem |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index ee2fb9d37745..992701d4d4f8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -1722,16 +1722,46 @@ config RELOCATABLE | |||
1722 | 1722 | ||
1723 | Note: If CONFIG_RELOCATABLE=y, then the kernel runs from the address | 1723 | Note: If CONFIG_RELOCATABLE=y, then the kernel runs from the address |
1724 | it has been loaded at and the compile time physical address | 1724 | it has been loaded at and the compile time physical address |
1725 | (CONFIG_PHYSICAL_START) is ignored. | 1725 | (CONFIG_PHYSICAL_START) is used as the minimum location. |
1726 | 1726 | ||
1727 | # Relocation on x86-32 needs some additional build support | 1727 | config RANDOMIZE_BASE |
1728 | bool "Randomize the address of the kernel image" | ||
1729 | depends on RELOCATABLE | ||
1730 | depends on !HIBERNATION | ||
1731 | default n | ||
1732 | ---help--- | ||
1733 | Randomizes the physical and virtual address at which the | ||
1734 | kernel image is decompressed, as a security feature that | ||
1735 | deters exploit attempts relying on knowledge of the location | ||
1736 | of kernel internals. | ||
1737 | |||
1738 | Entropy is generated using the RDRAND instruction if it | ||
1739 | is supported. If not, then RDTSC is used, if supported. If | ||
1740 | neither RDRAND nor RDTSC are supported, then no randomness | ||
1741 | is introduced. | ||
1742 | |||
1743 | The kernel will be offset by up to RANDOMIZE_BASE_MAX_OFFSET, | ||
1744 | and aligned according to PHYSICAL_ALIGN. | ||
1745 | |||
1746 | config RANDOMIZE_BASE_MAX_OFFSET | ||
1747 | hex "Maximum ASLR offset allowed" | ||
1748 | depends on RANDOMIZE_BASE | ||
1749 | default "0x10000000" | ||
1750 | range 0x0 0x10000000 | ||
1751 | ---help--- | ||
1752 | Determines the maximal offset in bytes that will be applied to the | ||
1753 | kernel when Address Space Layout Randomization (ASLR) is active. | ||
1754 | Must be less than or equal to the actual physical memory on the | ||
1755 | system. This must be a power of two. | ||
1756 | |||
1757 | # Relocation on x86 needs some additional build support | ||
1728 | config X86_NEED_RELOCS | 1758 | config X86_NEED_RELOCS |
1729 | def_bool y | 1759 | def_bool y |
1730 | depends on X86_32 && RELOCATABLE | 1760 | depends on RANDOMIZE_BASE || (X86_32 && RELOCATABLE) |
1731 | 1761 | ||
1732 | config PHYSICAL_ALIGN | 1762 | config PHYSICAL_ALIGN |
1733 | hex "Alignment value to which kernel should be aligned" | 1763 | hex "Alignment value to which kernel should be aligned" |
1734 | default "0x1000000" | 1764 | default "0x200000" |
1735 | range 0x2000 0x1000000 if X86_32 | 1765 | range 0x2000 0x1000000 if X86_32 |
1736 | range 0x200000 0x1000000 if X86_64 | 1766 | range 0x200000 0x1000000 if X86_64 |
1737 | ---help--- | 1767 | ---help--- |
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile index 3312f1be9fd9..ae8b5dbbd8c5 100644 --- a/arch/x86/boot/compressed/Makefile +++ b/arch/x86/boot/compressed/Makefile | |||
@@ -27,7 +27,7 @@ HOST_EXTRACFLAGS += -I$(srctree)/tools/include | |||
27 | 27 | ||
28 | VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \ | 28 | VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \ |
29 | $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \ | 29 | $(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \ |
30 | $(obj)/piggy.o $(obj)/cpuflags.o | 30 | $(obj)/piggy.o $(obj)/cpuflags.o $(obj)/aslr.o |
31 | 31 | ||
32 | $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone | 32 | $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone |
33 | 33 | ||
diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c new file mode 100644 index 000000000000..b73cc66d201e --- /dev/null +++ b/arch/x86/boot/compressed/aslr.c | |||
@@ -0,0 +1,23 @@ | |||
1 | #include "misc.h" | ||
2 | |||
3 | #ifdef CONFIG_RANDOMIZE_BASE | ||
4 | |||
5 | unsigned char *choose_kernel_location(unsigned char *input, | ||
6 | unsigned long input_size, | ||
7 | unsigned char *output, | ||
8 | unsigned long output_size) | ||
9 | { | ||
10 | unsigned long choice = (unsigned long)output; | ||
11 | |||
12 | if (cmdline_find_option_bool("nokaslr")) { | ||
13 | debug_putstr("KASLR disabled...\n"); | ||
14 | goto out; | ||
15 | } | ||
16 | |||
17 | /* XXX: choose random location. */ | ||
18 | |||
19 | out: | ||
20 | return (unsigned char *)choice; | ||
21 | } | ||
22 | |||
23 | #endif /* CONFIG_RANDOMIZE_BASE */ | ||
diff --git a/arch/x86/boot/compressed/cmdline.c b/arch/x86/boot/compressed/cmdline.c index bffd73b45b1f..b68e3033e6b9 100644 --- a/arch/x86/boot/compressed/cmdline.c +++ b/arch/x86/boot/compressed/cmdline.c | |||
@@ -1,6 +1,6 @@ | |||
1 | #include "misc.h" | 1 | #include "misc.h" |
2 | 2 | ||
3 | #ifdef CONFIG_EARLY_PRINTK | 3 | #if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE |
4 | 4 | ||
5 | static unsigned long fs; | 5 | static unsigned long fs; |
6 | static inline void set_fs(unsigned long seg) | 6 | static inline void set_fs(unsigned long seg) |
diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index 5d6f6891b188..9116aac232c7 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S | |||
@@ -117,9 +117,11 @@ preferred_addr: | |||
117 | addl %eax, %ebx | 117 | addl %eax, %ebx |
118 | notl %eax | 118 | notl %eax |
119 | andl %eax, %ebx | 119 | andl %eax, %ebx |
120 | #else | 120 | cmpl $LOAD_PHYSICAL_ADDR, %ebx |
121 | movl $LOAD_PHYSICAL_ADDR, %ebx | 121 | jge 1f |
122 | #endif | 122 | #endif |
123 | movl $LOAD_PHYSICAL_ADDR, %ebx | ||
124 | 1: | ||
123 | 125 | ||
124 | /* Target address to relocate to for decompression */ | 126 | /* Target address to relocate to for decompression */ |
125 | addl $z_extract_offset, %ebx | 127 | addl $z_extract_offset, %ebx |
@@ -191,14 +193,14 @@ relocated: | |||
191 | leal boot_heap(%ebx), %eax | 193 | leal boot_heap(%ebx), %eax |
192 | pushl %eax /* heap area */ | 194 | pushl %eax /* heap area */ |
193 | pushl %esi /* real mode pointer */ | 195 | pushl %esi /* real mode pointer */ |
194 | call decompress_kernel | 196 | call decompress_kernel /* returns kernel location in %eax */ |
195 | addl $24, %esp | 197 | addl $24, %esp |
196 | 198 | ||
197 | /* | 199 | /* |
198 | * Jump to the decompressed kernel. | 200 | * Jump to the decompressed kernel. |
199 | */ | 201 | */ |
200 | xorl %ebx, %ebx | 202 | xorl %ebx, %ebx |
201 | jmp *%ebp | 203 | jmp *%eax |
202 | 204 | ||
203 | /* | 205 | /* |
204 | * Stack and heap for uncompression | 206 | * Stack and heap for uncompression |
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index c337422b575d..c5c1ae0997e7 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S | |||
@@ -94,9 +94,11 @@ ENTRY(startup_32) | |||
94 | addl %eax, %ebx | 94 | addl %eax, %ebx |
95 | notl %eax | 95 | notl %eax |
96 | andl %eax, %ebx | 96 | andl %eax, %ebx |
97 | #else | 97 | cmpl $LOAD_PHYSICAL_ADDR, %ebx |
98 | movl $LOAD_PHYSICAL_ADDR, %ebx | 98 | jge 1f |
99 | #endif | 99 | #endif |
100 | movl $LOAD_PHYSICAL_ADDR, %ebx | ||
101 | 1: | ||
100 | 102 | ||
101 | /* Target address to relocate to for decompression */ | 103 | /* Target address to relocate to for decompression */ |
102 | addl $z_extract_offset, %ebx | 104 | addl $z_extract_offset, %ebx |
@@ -269,9 +271,11 @@ preferred_addr: | |||
269 | addq %rax, %rbp | 271 | addq %rax, %rbp |
270 | notq %rax | 272 | notq %rax |
271 | andq %rax, %rbp | 273 | andq %rax, %rbp |
272 | #else | 274 | cmpq $LOAD_PHYSICAL_ADDR, %rbp |
273 | movq $LOAD_PHYSICAL_ADDR, %rbp | 275 | jge 1f |
274 | #endif | 276 | #endif |
277 | movq $LOAD_PHYSICAL_ADDR, %rbp | ||
278 | 1: | ||
275 | 279 | ||
276 | /* Target address to relocate to for decompression */ | 280 | /* Target address to relocate to for decompression */ |
277 | leaq z_extract_offset(%rbp), %rbx | 281 | leaq z_extract_offset(%rbp), %rbx |
@@ -339,13 +343,13 @@ relocated: | |||
339 | movl $z_input_len, %ecx /* input_len */ | 343 | movl $z_input_len, %ecx /* input_len */ |
340 | movq %rbp, %r8 /* output target address */ | 344 | movq %rbp, %r8 /* output target address */ |
341 | movq $z_output_len, %r9 /* decompressed length */ | 345 | movq $z_output_len, %r9 /* decompressed length */ |
342 | call decompress_kernel | 346 | call decompress_kernel /* returns kernel location in %rax */ |
343 | popq %rsi | 347 | popq %rsi |
344 | 348 | ||
345 | /* | 349 | /* |
346 | * Jump to the decompressed kernel. | 350 | * Jump to the decompressed kernel. |
347 | */ | 351 | */ |
348 | jmp *%rbp | 352 | jmp *%rax |
349 | 353 | ||
350 | .code32 | 354 | .code32 |
351 | no_longmode: | 355 | no_longmode: |
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index 434f077d2c4d..71387685dc16 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c | |||
@@ -395,7 +395,7 @@ static void parse_elf(void *output) | |||
395 | free(phdrs); | 395 | free(phdrs); |
396 | } | 396 | } |
397 | 397 | ||
398 | asmlinkage void decompress_kernel(void *rmode, memptr heap, | 398 | asmlinkage void *decompress_kernel(void *rmode, memptr heap, |
399 | unsigned char *input_data, | 399 | unsigned char *input_data, |
400 | unsigned long input_len, | 400 | unsigned long input_len, |
401 | unsigned char *output, | 401 | unsigned char *output, |
@@ -422,6 +422,10 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap, | |||
422 | free_mem_ptr = heap; /* Heap */ | 422 | free_mem_ptr = heap; /* Heap */ |
423 | free_mem_end_ptr = heap + BOOT_HEAP_SIZE; | 423 | free_mem_end_ptr = heap + BOOT_HEAP_SIZE; |
424 | 424 | ||
425 | output = choose_kernel_location(input_data, input_len, | ||
426 | output, output_len); | ||
427 | |||
428 | /* Validate memory location choices. */ | ||
425 | if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) | 429 | if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1)) |
426 | error("Destination address inappropriately aligned"); | 430 | error("Destination address inappropriately aligned"); |
427 | #ifdef CONFIG_X86_64 | 431 | #ifdef CONFIG_X86_64 |
@@ -441,5 +445,5 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap, | |||
441 | parse_elf(output); | 445 | parse_elf(output); |
442 | handle_relocations(output, output_len); | 446 | handle_relocations(output, output_len); |
443 | debug_putstr("done.\nBooting the kernel.\n"); | 447 | debug_putstr("done.\nBooting the kernel.\n"); |
444 | return; | 448 | return output; |
445 | } | 449 | } |
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h index 674019d8e235..9077af7fd0b8 100644 --- a/arch/x86/boot/compressed/misc.h +++ b/arch/x86/boot/compressed/misc.h | |||
@@ -39,23 +39,38 @@ static inline void debug_putstr(const char *s) | |||
39 | 39 | ||
40 | #endif | 40 | #endif |
41 | 41 | ||
42 | #ifdef CONFIG_EARLY_PRINTK | 42 | #if CONFIG_EARLY_PRINTK || CONFIG_RANDOMIZE_BASE |
43 | |||
44 | /* cmdline.c */ | 43 | /* cmdline.c */ |
45 | int cmdline_find_option(const char *option, char *buffer, int bufsize); | 44 | int cmdline_find_option(const char *option, char *buffer, int bufsize); |
46 | int cmdline_find_option_bool(const char *option); | 45 | int cmdline_find_option_bool(const char *option); |
46 | #endif | ||
47 | 47 | ||
48 | /* early_serial_console.c */ | ||
49 | extern int early_serial_base; | ||
50 | void console_init(void); | ||
51 | 48 | ||
49 | #if CONFIG_RANDOMIZE_BASE | ||
50 | /* aslr.c */ | ||
51 | unsigned char *choose_kernel_location(unsigned char *input, | ||
52 | unsigned long input_size, | ||
53 | unsigned char *output, | ||
54 | unsigned long output_size); | ||
52 | #else | 55 | #else |
56 | static inline | ||
57 | unsigned char *choose_kernel_location(unsigned char *input, | ||
58 | unsigned long input_size, | ||
59 | unsigned char *output, | ||
60 | unsigned long output_size) | ||
61 | { | ||
62 | return output; | ||
63 | } | ||
64 | #endif | ||
53 | 65 | ||
66 | #ifdef CONFIG_EARLY_PRINTK | ||
54 | /* early_serial_console.c */ | 67 | /* early_serial_console.c */ |
68 | extern int early_serial_base; | ||
69 | void console_init(void); | ||
70 | #else | ||
55 | static const int early_serial_base; | 71 | static const int early_serial_base; |
56 | static inline void console_init(void) | 72 | static inline void console_init(void) |
57 | { } | 73 | { } |
58 | |||
59 | #endif | 74 | #endif |
60 | 75 | ||
61 | #endif | 76 | #endif |