From: Baoquan He <b...@redhat.com> This patch exchanges the prior slots[] array for the new slot_areas[] array, and lifts the limitation of KERNEL_IMAGE_SIZE on the physical address offset for 64-bit. As before, process_e820_entry walks memory and populates slot_areas[], splitting on any detected mem_avoid collisions.
Signed-off-by: Baoquan He <b...@redhat.com> [kees: rewrote changelog, refactored goto into while, limit 32-bit to 1G] Signed-off-by: Kees Cook <keesc...@chromium.org> --- arch/x86/boot/compressed/aslr.c | 92 ++++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/arch/x86/boot/compressed/aslr.c b/arch/x86/boot/compressed/aslr.c index 53ceaa0a08b9..0587eac3e05d 100644 --- a/arch/x86/boot/compressed/aslr.c +++ b/arch/x86/boot/compressed/aslr.c @@ -335,25 +335,42 @@ static void slots_append(unsigned long addr) static unsigned long slots_fetch_random(void) { + unsigned long random; + int i; + /* Handle case of no slots stored. */ if (slot_max == 0) return 0; - return slots[get_random_long("Physical") % slot_max]; + random = get_random_long("Physical") % slot_max; + + for (i = 0; i < slot_area_index; i++) { + if (random >= slot_areas[i].num) { + random -= slot_areas[i].num; + continue; + } + return slot_areas[i].addr + random * CONFIG_PHYSICAL_ALIGN; + } + + if (i == slot_area_index) + debug_putstr("slots_fetch_random() failed!?\n"); + return 0; } static void process_e820_entry(struct e820entry *entry, unsigned long minimum, unsigned long image_size) { - struct mem_vector region, img; + struct mem_vector region, out; + struct slot_area slot_area; + unsigned long min, start_orig; /* Skip non-RAM entries. */ if (entry->type != E820_RAM) return; - /* Ignore entries entirely above our maximum. */ - if (entry->addr >= KERNEL_IMAGE_SIZE) + /* On 32-bit, ignore entries entirely above our maximum. */ + if (IS_ENABLED(CONFIG_X86_32) && entry->addr >= KERNEL_IMAGE_SIZE) return; /* Ignore entries entirely below our minimum. */ @@ -363,31 +380,54 @@ static void process_e820_entry(struct e820entry *entry, region.start = entry->addr; region.size = entry->size; - /* Potentially raise address to minimum location. */ - if (region.start < minimum) - region.start = minimum; + /* Give up if slot area array is full. */ + while (slot_area_index < MAX_SLOT_AREA) { + start_orig = region.start; - /* Potentially raise address to meet alignment requirements. */ - region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN); + /* Potentially raise address to minimum location. */ + if (region.start < minimum) + region.start = minimum; - /* Did we raise the address above the bounds of this e820 region? */ - if (region.start > entry->addr + entry->size) - return; + /* Potentially raise address to meet alignment needs. */ + region.start = ALIGN(region.start, CONFIG_PHYSICAL_ALIGN); - /* Reduce size by any delta from the original address. */ - region.size -= region.start - entry->addr; + /* Did we raise the address above this e820 region? */ + if (region.start > entry->addr + entry->size) + return; - /* Reduce maximum size to fit end of image within maximum limit. */ - if (region.start + region.size > KERNEL_IMAGE_SIZE) - region.size = KERNEL_IMAGE_SIZE - region.start; + /* Reduce size by any delta from the original address. */ + region.size -= region.start - start_orig; - /* Walk each aligned slot and check for avoided areas. */ - for (img.start = region.start, img.size = image_size ; - mem_contains(®ion, &img) ; - img.start += CONFIG_PHYSICAL_ALIGN) { - if (mem_avoid_overlap(&img)) - continue; - slots_append(img.start); + /* On 32-bit, reduce region size to fit within max size. */ + if (IS_ENABLED(CONFIG_X86_32) && + region.start + region.size > KERNEL_IMAGE_SIZE) + region.size = KERNEL_IMAGE_SIZE - region.start; + + /* Return if region can't contain decompressed kernel */ + if (region.size < image_size) + return; + + /* If nothing overlaps, store the region and return. */ + if (!mem_avoid_overlap(®ion)) { + store_slot_info(®ion, image_size); + return; + } + + /* Other wise, find the lowest overlap. */ + min = mem_min_overlap(®ion, &out); + + /* Store the region if it can hold at least image_size. */ + if (min > region.start + image_size) { + struct mem_vector tmp; + + tmp.start = region.start; + tmp.size = min - region.start; + store_slot_info(&tmp, image_size); + } + + /* Clip off the overlapping region and start over. */ + region.size -= out.start - region.start + out.size; + region.start = out.start + out.size; } } @@ -403,6 +443,10 @@ static unsigned long find_random_phy_addr(unsigned long minimum, /* Verify potential e820 positions, appending to slots list. */ for (i = 0; i < real_mode->e820_entries; i++) { process_e820_entry(&real_mode->e820_map[i], minimum, size); + if (slot_area_index == MAX_SLOT_AREA) { + debug_putstr("Aborted e820 scan (slot_areas full)!\n"); + break; + } } return slots_fetch_random(); -- 2.6.3