Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kees Cook and committed by
H. Peter Anvin
82fa9637 5bfce5ef

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