aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2013-10-10 20:18:16 -0400
committerH. Peter Anvin <hpa@linux.intel.com>2013-10-13 06:12:19 -0400
commit82fa9637a2ba285bcc7c5050c73010b2c1b3d803 (patch)
treef9a1ba0cd4670beaa0f5b146a979d9dda0165f1c /arch/x86
parent5bfce5ef55cbe78ee2ee6e97f2e26a8a582008f3 (diff)
x86, kaslr: Select random position from e820 maps
Counts available alignment positions across all e820 maps, and chooses one randomly for the new kernel base address, making sure not to collide with unsafe memory areas. Signed-off-by: Kees Cook <keescook@chromium.org> Link: http://lkml.kernel.org/r/1381450698-28710-5-git-send-email-keescook@chromium.org Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Diffstat (limited to 'arch/x86')
-rw-r--r--arch/x86/boot/compressed/aslr.c193
-rw-r--r--arch/x86/boot/compressed/misc.c10
-rw-r--r--arch/x86/boot/compressed/misc.h8
3 files changed, 202 insertions, 9 deletions
diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c
index 14b24e0e5496..05957986d123 100644
--- a/arch/x86/boot/compressed/aslr.c
+++ b/arch/x86/boot/compressed/aslr.c
@@ -3,6 +3,7 @@
3#ifdef CONFIG_RANDOMIZE_BASE 3#ifdef CONFIG_RANDOMIZE_BASE
4#include <asm/msr.h> 4#include <asm/msr.h>
5#include <asm/archrandom.h> 5#include <asm/archrandom.h>
6#include <asm/e820.h>
6 7
7#define I8254_PORT_CONTROL 0x43 8#define I8254_PORT_CONTROL 0x43
8#define I8254_PORT_COUNTER0 0x40 9#define I8254_PORT_COUNTER0 0x40
@@ -55,20 +56,210 @@ static unsigned long get_random_long(void)
55 return random; 56 return random;
56} 57}
57 58
59struct mem_vector {
60 unsigned long start;
61 unsigned long size;
62};
63
64#define MEM_AVOID_MAX 5
65struct mem_vector mem_avoid[MEM_AVOID_MAX];
66
67static bool mem_contains(struct mem_vector *region, struct mem_vector *item)
68{
69 /* Item at least partially before region. */
70 if (item->start < region->start)
71 return false;
72 /* Item at least partially after region. */
73 if (item->start + item->size > region->start + region->size)
74 return false;
75 return true;
76}
77
78static bool mem_overlaps(struct mem_vector *one, struct mem_vector *two)
79{
80 /* Item one is entirely before item two. */
81 if (one->start + one->size <= two->start)
82 return false;
83 /* Item one is entirely after item two. */
84 if (one->start >= two->start + two->size)
85 return false;
86 return true;
87}
88
89static void mem_avoid_init(unsigned long input, unsigned long input_size,
90 unsigned long output, unsigned long output_size)
91{
92 u64 initrd_start, initrd_size;
93 u64 cmd_line, cmd_line_size;
94 unsigned long unsafe, unsafe_len;
95 char *ptr;
96
97 /*
98 * Avoid the region that is unsafe to overlap during
99 * decompression (see calculations at top of misc.c).
100 */
101 unsafe_len = (output_size >> 12) + 32768 + 18;
102 unsafe = (unsigned long)input + input_size - unsafe_len;
103 mem_avoid[0].start = unsafe;
104 mem_avoid[0].size = unsafe_len;
105
106 /* Avoid initrd. */
107 initrd_start = (u64)real_mode->ext_ramdisk_image << 32;
108 initrd_start |= real_mode->hdr.ramdisk_image;
109 initrd_size = (u64)real_mode->ext_ramdisk_size << 32;
110 initrd_size |= real_mode->hdr.ramdisk_size;
111 mem_avoid[1].start = initrd_start;
112 mem_avoid[1].size = initrd_size;
113
114 /* Avoid kernel command line. */
115 cmd_line = (u64)real_mode->ext_cmd_line_ptr << 32;
116 cmd_line |= real_mode->hdr.cmd_line_ptr;
117 /* Calculate size of cmd_line. */
118 ptr = (char *)(unsigned long)cmd_line;
119 for (cmd_line_size = 0; ptr[cmd_line_size++]; )
120 ;
121 mem_avoid[2].start = cmd_line;
122 mem_avoid[2].size = cmd_line_size;
123
124 /* Avoid heap memory. */
125 mem_avoid[3].start = (unsigned long)free_mem_ptr;
126 mem_avoid[3].size = BOOT_HEAP_SIZE;
127
128 /* Avoid stack memory. */
129 mem_avoid[4].start = (unsigned long)free_mem_end_ptr;
130 mem_avoid[4].size = BOOT_STACK_SIZE;
131}
132
133/* Does this memory vector overlap a known avoided area? */
134bool mem_avoid_overlap(struct mem_vector *img)
135{
136 int i;
137
138 for (i = 0; i < MEM_AVOID_MAX; i++) {
139 if (mem_overlaps(img, &mem_avoid[i]))
140 return true;
141 }
142
143 return false;
144}
145
146unsigned long slots[CONFIG_RANDOMIZE_BASE_MAX_OFFSET / CONFIG_PHYSICAL_ALIGN];
147unsigned long slot_max = 0;
148
149static void slots_append(unsigned long addr)
150{
151 /* Overflowing the slots list should be impossible. */
152 if (slot_max >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET /
153 CONFIG_PHYSICAL_ALIGN)
154 return;
155
156 slots[slot_max++] = addr;
157}
158
159static unsigned long slots_fetch_random(void)
160{
161 /* Handle case of no slots stored. */
162 if (slot_max == 0)
163 return 0;
164
165 return slots[get_random_long() % slot_max];
166}
167
168static void process_e820_entry(struct e820entry *entry,
169 unsigned long minimum,
170 unsigned long image_size)
171{
172 struct mem_vector region, img;
173
174 /* Skip non-RAM entries. */
175 if (entry->type != E820_RAM)
176 return;
177
178 /* Ignore entries entirely above our maximum. */
179 if (entry->addr >= CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
180 return;
181
182 /* Ignore entries entirely below our minimum. */
183 if (entry->addr + entry->size < minimum)
184 return;
185
186 region.start = entry->addr;
187 region.size = entry->size;
188
189 /* Potentially raise address to minimum location. */
190 if (region.start < minimum)
191 region.start = minimum;
192
193 /* Potentially raise address to meet alignment requirements. */
194 region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN);
195
196 /* Did we raise the address above the bounds of this e820 region? */
197 if (region.start > entry->addr + entry->size)
198 return;
199
200 /* Reduce size by any delta from the original address. */
201 region.size -= region.start - entry->addr;
202
203 /* Reduce maximum size to fit end of image within maximum limit. */
204 if (region.start + region.size > CONFIG_RANDOMIZE_BASE_MAX_OFFSET)
205 region.size = CONFIG_RANDOMIZE_BASE_MAX_OFFSET - region.start;
206
207 /* Walk each aligned slot and check for avoided areas. */
208 for (img.start = region.start, img.size = image_size ;
209 mem_contains(&region, &img) ;
210 img.start += CONFIG_PHYSICAL_ALIGN) {
211 if (mem_avoid_overlap(&img))
212 continue;
213 slots_append(img.start);
214 }
215}
216
217static unsigned long find_random_addr(unsigned long minimum,
218 unsigned long size)
219{
220 int i;
221 unsigned long addr;
222
223 /* Make sure minimum is aligned. */
224 minimum = ALIGN(minimum, CONFIG_PHYSICAL_ALIGN);
225
226 /* Verify potential e820 positions, appending to slots list. */
227 for (i = 0; i < real_mode->e820_entries; i++) {
228 process_e820_entry(&real_mode->e820_map[i], minimum, size);
229 }
230
231 return slots_fetch_random();
232}
233
58unsigned char *choose_kernel_location(unsigned char *input, 234unsigned char *choose_kernel_location(unsigned char *input,
59 unsigned long input_size, 235 unsigned long input_size,
60 unsigned char *output, 236 unsigned char *output,
61 unsigned long output_size) 237 unsigned long output_size)
62{ 238{
63 unsigned long choice = (unsigned long)output; 239 unsigned long choice = (unsigned long)output;
240 unsigned long random;
64 241
65 if (cmdline_find_option_bool("nokaslr")) { 242 if (cmdline_find_option_bool("nokaslr")) {
66 debug_putstr("KASLR disabled...\n"); 243 debug_putstr("KASLR disabled...\n");
67 goto out; 244 goto out;
68 } 245 }
69 246
70 /* XXX: choose random location. */ 247 /* Record the various known unsafe memory ranges. */
248 mem_avoid_init((unsigned long)input, input_size,
249 (unsigned long)output, output_size);
250
251 /* Walk e820 and find a random address. */
252 random = find_random_addr(choice, output_size);
253 if (!random) {
254 debug_putstr("KASLR could not find suitable E820 region...\n");
255 goto out;
256 }
257
258 /* Always enforce the minimum. */
259 if (random < choice)
260 goto out;
71 261
262 choice = random;
72out: 263out:
73 return (unsigned char *)choice; 264 return (unsigned char *)choice;
74} 265}
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 71387685dc16..196eaf373a06 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -112,14 +112,8 @@ struct boot_params *real_mode; /* Pointer to real-mode data */
112void *memset(void *s, int c, size_t n); 112void *memset(void *s, int c, size_t n);
113void *memcpy(void *dest, const void *src, size_t n); 113void *memcpy(void *dest, const void *src, size_t n);
114 114
115#ifdef CONFIG_X86_64 115memptr free_mem_ptr;
116#define memptr long 116memptr free_mem_end_ptr;
117#else
118#define memptr unsigned
119#endif
120
121static memptr free_mem_ptr;
122static memptr free_mem_end_ptr;
123 117
124static char *vidmem; 118static char *vidmem;
125static int vidport; 119static int vidport;
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 0782eb0b6e30..24e3e569a13c 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -23,7 +23,15 @@
23#define BOOT_BOOT_H 23#define BOOT_BOOT_H
24#include "../ctype.h" 24#include "../ctype.h"
25 25
26#ifdef CONFIG_X86_64
27#define memptr long
28#else
29#define memptr unsigned
30#endif
31
26/* misc.c */ 32/* misc.c */
33extern memptr free_mem_ptr;
34extern memptr free_mem_end_ptr;
27extern struct boot_params *real_mode; /* Pointer to real-mode data */ 35extern struct boot_params *real_mode; /* Pointer to real-mode data */
28void __putstr(const char *s); 36void __putstr(const char *s);
29#define error_putstr(__x) __putstr(__x) 37#define error_putstr(__x) __putstr(__x)