Skip to content
  • Yinghai Lu's avatar
    x86/boot: Fix "run_size" calculation · 67b66625
    Yinghai Lu authored
    
    
    Currently, the "run_size" variable holds the total kernel size
    (size of code plus brk and bss) and is calculated via the shell script
    arch/x86/tools/calc_run_size.sh. It gets the file offset and mem size
    of the .bss and .brk sections from the vmlinux, and adds them as follows:
    
      run_size = $(( $offsetA + $sizeA + $sizeB ))
    
    However, this is not correct (it is too large). To illustrate, here's
    a walk-through of the script's calculation, compared to the correct way
    to find it.
    
    First, offsetA is found as the starting address of the first .bss or
    .brk section seen in the ELF file. The sizeA and sizeB values are the
    respective section sizes.
    
     [bhe@x1 linux]$ objdump -h vmlinux
    
     vmlinux:     file format elf64-x86-64
    
     Sections:
     Idx Name    Size      VMA               LMA               File off  Algn
      27 .bss    00170000  ffffffff81ec8000  0000000001ec8000  012c8000  2**12
                 ALLOC
      28 .brk    00027000  ffffffff82038000  0000000002038000  012c8000  2**0
                 ALLOC
    
    Here, offsetA is 0x012c8000, with sizeA at 0x00170000 and sizeB at
    0x00027000. The resulting run_size is 0x145f000:
    
     0x012c8000 + 0x00170000 + 0x00027000 = 0x145f000
    
    However, if we instead examine the ELF LOAD program headers, we see a
    different picture.
    
     [bhe@x1 linux]$ readelf -l vmlinux
    
     Elf file type is EXEC (Executable file)
     Entry point 0x1000000
     There are 5 program headers, starting at offset 64
    
     Program Headers:
      Type        Offset             VirtAddr           PhysAddr
                  FileSiz            MemSiz              Flags  Align
      LOAD        0x0000000000200000 0xffffffff81000000 0x0000000001000000
                  0x0000000000b5e000 0x0000000000b5e000  R E    200000
      LOAD        0x0000000000e00000 0xffffffff81c00000 0x0000000001c00000
                  0x0000000000145000 0x0000000000145000  RW     200000
      LOAD        0x0000000001000000 0x0000000000000000 0x0000000001d45000
                  0x0000000000018158 0x0000000000018158  RW     200000
      LOAD        0x000000000115e000 0xffffffff81d5e000 0x0000000001d5e000
                  0x000000000016a000 0x0000000000301000  RWE    200000
      NOTE        0x000000000099bcac 0xffffffff8179bcac 0x000000000179bcac
                  0x00000000000001bc 0x00000000000001bc         4
    
     Section to Segment mapping:
      Segment Sections...
       00     .text .notes __ex_table .rodata __bug_table .pci_fixup .tracedata
              __ksymtab __ksymtab_gpl __ksymtab_strings __init_rodata __param
              __modver
       01     .data .vvar
       02     .data..percpu
       03     .init.text .init.data .x86_cpu_dev.init .parainstructions
              .altinstructions .altinstr_replacement .iommu_table .apicdrivers
              .exit.text .smp_locks .bss .brk
       04     .notes
    
    As mentioned, run_size needs to be the size of the running kernel
    including .bss and .brk. We can see from the Section/Segment mapping
    above that .bss and .brk are included in segment 03 (which corresponds
    to the final LOAD program header). To find the run_size, we calculate
    the end of the LOAD segment from its PhysAddr start (0x0000000001d5e000)
    and its MemSiz (0x0000000000301000), minus the physical load address of
    the kernel (the first LOAD segment's PhysAddr: 0x0000000001000000). The
    resulting run_size is 0x105f000:
    
     0x0000000001d5e000 + 0x0000000000301000 - 0x0000000001000000 = 0x105f000
    
    So, from this we can see that the existing run_size calculation is
    0x400000 too high. And, as it turns out, the correct run_size is
    actually equal to VO_end - VO_text, which is certainly easier to calculate.
    _end: 0xffffffff8205f000
    _text:0xffffffff81000000
    
     0xffffffff8205f000 - 0xffffffff81000000 = 0x105f000
    
    As a result, run_size is a simple constant, so we don't need to pass it
    around; we already have voffset.h for such things. We can share voffset.h
    between misc.c and header.S instead of getting run_size in other ways.
    This patch moves voffset.h creation code to boot/compressed/Makefile,
    and switches misc.c to use the VO_end - VO_text calculation for run_size.
    
    Dependence before:
    
     boot/header.S ==> boot/voffset.h ==> vmlinux
     boot/header.S ==> compressed/vmlinux ==> compressed/misc.c
    
    Dependence after:
    
     boot/header.S ==> compressed/vmlinux ==> compressed/misc.c ==> boot/voffset.h ==> vmlinux
    
    Signed-off-by: default avatarYinghai Lu <yinghai@kernel.org>
    Signed-off-by: default avatarBaoquan He <bhe@redhat.com>
    [ Rewrote the changelog. ]
    Signed-off-by: default avatarKees Cook <keescook@chromium.org>
    Cc: Andrew Morton <akpm@linux-foundation.org>
    Cc: Andy Lutomirski <luto@amacapital.net>
    Cc: Andy Lutomirski <luto@kernel.org>
    Cc: Borislav Petkov <bp@alien8.de>
    Cc: Brian Gerst <brgerst@gmail.com>
    Cc: Dave Young <dyoung@redhat.com>
    Cc: Denys Vlasenko <dvlasenk@redhat.com>
    Cc: H. Peter Anvin <hpa@zytor.com>
    Cc: Josh Triplett <josh@joshtriplett.org>
    Cc: Junjie Mao <eternal.n08@gmail.com>
    Cc: Linus Torvalds <torvalds@linux-foundation.org>
    Cc: Peter Zijlstra <peterz@infradead.org>
    Cc: Thomas Gleixner <tglx@linutronix.de>
    Cc: Vivek Goyal <vgoyal@redhat.com>
    Cc: lasse.collin@tukaani.org
    Fixes: e6023367 ("x86, kaslr: Prevent .bss from overlaping initrd")
    Link: http://lkml.kernel.org/r/1461888548-32439-5-git-send-email-keescook@chromium.org
    
    
    Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
    67b66625