]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
x86, kaslr: Prevent .bss from overlaping initrd
authorJunjie Mao <eternal.n08@gmail.com>
Fri, 31 Oct 2014 13:40:38 +0000 (21:40 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 21 Nov 2014 17:23:04 +0000 (09:23 -0800)
commit e6023367d779060fddc9a52d1f474085b2b36298 upstream.

When choosing a random address, the current implementation does not take into
account the reversed space for .bss and .brk sections. Thus the relocated kernel
may overlap other components in memory. Here is an example of the overlap from a
x86_64 kernel in qemu (the ranges of physical addresses are presented):

 Physical Address

    0x0fe00000                  --+--------------------+  <-- randomized base
                               /  |  relocated kernel  |
                   vmlinux.bin    | (from vmlinux.bin) |
    0x1336d000    (an ELF file)   +--------------------+--
                               \  |                    |  \
    0x1376d870                  --+--------------------+   |
                                  |    relocs table    |   |
    0x13c1c2a8                    +--------------------+   .bss and .brk
                                  |                    |   |
    0x13ce6000                    +--------------------+   |
                                  |                    |  /
    0x13f77000                    |       initrd       |--
                                  |                    |
    0x13fef374                    +--------------------+

The initrd image will then be overwritten by the memset during early
initialization:

[    1.655204] Unpacking initramfs...
[    1.662831] Initramfs unpacking failed: junk in compressed archive

This patch prevents the above situation by requiring a larger space when looking
for a random kernel base, so that existing logic can effectively avoids the
overlap.

[kees: switched to perl to avoid hex translation pain in mawk vs gawk]
[kees: calculated overlap without relocs table]

Fixes: 82fa9637a2 ("x86, kaslr: Select random position from e820 maps")
Reported-by: Fengguang Wu <fengguang.wu@intel.com>
Signed-off-by: Junjie Mao <eternal.n08@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Cc: Josh Triplett <josh@joshtriplett.org>
Cc: Matt Fleming <matt.fleming@intel.com>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Andi Kleen <ak@linux.intel.com>
Link: http://lkml.kernel.org/r/1414762838-13067-1-git-send-email-eternal.n08@gmail.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/head_32.S
arch/x86/boot/compressed/head_64.S
arch/x86/boot/compressed/misc.c
arch/x86/boot/compressed/mkpiggy.c
arch/x86/tools/calc_run_size.pl [new file with mode: 0644]

index 0fcd9133790c566423b313fccef95a0768bd0d07..14fe7cba21d11e040c56c28f48e15bcad5f0d0d1 100644 (file)
@@ -75,8 +75,10 @@ suffix-$(CONFIG_KERNEL_XZ)   := xz
 suffix-$(CONFIG_KERNEL_LZO)    := lzo
 suffix-$(CONFIG_KERNEL_LZ4)    := lz4
 
+RUN_SIZE = $(shell objdump -h vmlinux | \
+            perl $(srctree)/arch/x86/tools/calc_run_size.pl)
 quiet_cmd_mkpiggy = MKPIGGY $@
-      cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
+      cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
 
 targets += piggy.S
 $(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
index f45ab7a36fb6c48210ee9b408772f03a811f9293..c5b56ed10affec4ea5872efc6d050870bd15ebd2 100644 (file)
@@ -186,7 +186,8 @@ relocated:
  * Do the decompression, and jump to the new kernel..
  */
                                /* push arguments for decompress_kernel: */
-       pushl   $z_output_len   /* decompressed length */
+       pushl   $z_run_size     /* size of kernel with .bss and .brk */
+       pushl   $z_output_len   /* decompressed length, end of relocs */
        leal    z_extract_offset_negative(%ebx), %ebp
        pushl   %ebp            /* output address */
        pushl   $z_input_len    /* input_len */
@@ -196,7 +197,7 @@ relocated:
        pushl   %eax            /* heap area */
        pushl   %esi            /* real mode pointer */
        call    decompress_kernel /* returns kernel location in %eax */
-       addl    $24, %esp
+       addl    $28, %esp
 
 /*
  * Jump to the decompressed kernel.
index b10fa66a25400ca8955300672f89f142019df498..34bbc0911b7c79099e6bdc0c108c01cc88803eed 100644 (file)
@@ -334,13 +334,16 @@ relocated:
  * Do the decompression, and jump to the new kernel..
  */
        pushq   %rsi                    /* Save the real mode argument */
+       movq    $z_run_size, %r9        /* size of kernel with .bss and .brk */
+       pushq   %r9
        movq    %rsi, %rdi              /* real mode address */
        leaq    boot_heap(%rip), %rsi   /* malloc area for uncompression */
        leaq    input_data(%rip), %rdx  /* input_data */
        movl    $z_input_len, %ecx      /* input_len */
        movq    %rbp, %r8               /* output target address */
-       movq    $z_output_len, %r9      /* decompressed length */
+       movq    $z_output_len, %r9      /* decompressed length, end of relocs */
        call    decompress_kernel       /* returns kernel location in %rax */
+       popq    %r9
        popq    %rsi
 
 /*
index 196eaf373a06ad89c11bc9133a5c97b0e5ebc6d9..eb25ca1eb6dae28d49e79d0535126a90eddae780 100644 (file)
@@ -393,7 +393,8 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
                                  unsigned char *input_data,
                                  unsigned long input_len,
                                  unsigned char *output,
-                                 unsigned long output_len)
+                                 unsigned long output_len,
+                                 unsigned long run_size)
 {
        real_mode = rmode;
 
@@ -416,8 +417,14 @@ asmlinkage void *decompress_kernel(void *rmode, memptr heap,
        free_mem_ptr     = heap;        /* Heap */
        free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
 
-       output = choose_kernel_location(input_data, input_len,
-                                       output, output_len);
+       /*
+        * The memory hole needed for the kernel is the larger of either
+        * the entire decompressed kernel plus relocation table, or the
+        * entire decompressed kernel plus .bss and .brk sections.
+        */
+       output = choose_kernel_location(input_data, input_len, output,
+                                       output_len > run_size ? output_len
+                                                             : run_size);
 
        /* Validate memory location choices. */
        if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
index b669ab65bf6cf2e6973d6dc50cb693a1a8568324..d8222f213182f120c6f1d4c43e9ac477b2c2ca24 100644 (file)
@@ -36,11 +36,13 @@ int main(int argc, char *argv[])
        uint32_t olen;
        long ilen;
        unsigned long offs;
+       unsigned long run_size;
        FILE *f = NULL;
        int retval = 1;
 
-       if (argc < 2) {
-               fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
+       if (argc < 3) {
+               fprintf(stderr, "Usage: %s compressed_file run_size\n",
+                               argv[0]);
                goto bail;
        }
 
@@ -74,6 +76,7 @@ int main(int argc, char *argv[])
        offs += olen >> 12;     /* Add 8 bytes for each 32K block */
        offs += 64*1024 + 128;  /* Add 64K + 128 bytes slack */
        offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
+       run_size = atoi(argv[2]);
 
        printf(".section \".rodata..compressed\",\"a\",@progbits\n");
        printf(".globl z_input_len\n");
@@ -85,6 +88,8 @@ int main(int argc, char *argv[])
        /* z_extract_offset_negative allows simplification of head_32.S */
        printf(".globl z_extract_offset_negative\n");
        printf("z_extract_offset_negative = -0x%lx\n", offs);
+       printf(".globl z_run_size\n");
+       printf("z_run_size = %lu\n", run_size);
 
        printf(".globl input_data, input_data_end\n");
        printf("input_data:\n");
diff --git a/arch/x86/tools/calc_run_size.pl b/arch/x86/tools/calc_run_size.pl
new file mode 100644 (file)
index 0000000..0b0b124
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+#
+# Calculate the amount of space needed to run the kernel, including room for
+# the .bss and .brk sections.
+#
+# Usage:
+# objdump -h a.out | perl calc_run_size.pl
+use strict;
+
+my $mem_size = 0;
+my $file_offset = 0;
+
+my $sections=" *[0-9]+ \.(?:bss|brk) +";
+while (<>) {
+       if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
+               my $size = hex($1);
+               my $offset = hex($2);
+               $mem_size += $size;
+               if ($file_offset == 0) {
+                       $file_offset = $offset;
+               } elsif ($file_offset != $offset) {
+                       die ".bss and .brk lack common file offset\n";
+               }
+       }
+}
+
+if ($file_offset == 0) {
+       die "Never found .bss or .brk file offset\n";
+}
+printf("%d\n", $mem_size + $file_offset);