• Arvind Sankar's avatar
    x86/boot: Correct relocation destination on old linkers · 5214028d
    Arvind Sankar authored
    For the 32-bit kernel, as described in
    
      6d92bc9d ("x86/build: Build compressed x86 kernels as PIE"),
    
    pre-2.26 binutils generates R_386_32 relocations in PIE mode. Since the
    startup code does not perform relocation, any reloc entry with R_386_32
    will remain as 0 in the executing code.
    
    Commit
    
      974f221c ("x86/boot: Move compressed kernel to the end of the
                     decompression buffer")
    
    added a new symbol _end but did not mark it hidden, which doesn't give
    the correct offset on older linkers. This causes the compressed kernel
    to be copied beyond the end of the decompression buffer, rather than
    flush against it. This region of memory may be reserved or already
    allocated for other purposes by the bootloader.
    
    Mark _end as hidden to fix. This changes the relocation from R_386_32 to
    R_386_RELATIVE even on the pre-2.26 binutils.
    
    For 64-bit, this is not strictly necessary, as the 64-bit kernel is only
    built as PIE if the linker supports -z noreloc-overflow, which implies
    binutils-2.27+, but for consistency, mark _end as hidden here too.
    
    The below illustrates the before/after impact of the patch using
    binutils-2.25 and gcc-4.6.4 (locally compiled from source) and QEMU.
    
      Disassembly before patch:
        48:   8b 86 60 02 00 00       mov    0x260(%esi),%eax
        4e:   2d 00 00 00 00          sub    $0x0,%eax
                              4f: R_386_32    _end
      Disassembly after patch:
        48:   8b 86 60 02 00 00       mov    0x260(%esi),%eax
        4e:   2d 00 f0 76 00          sub    $0x76f000,%eax
                              4f: R_386_RELATIVE      *ABS*
    
    Dump from extract_kernel before patch:
    	early console in extract_kernel
    	input_data: 0x0207c098 <--- this is at output + init_size
    	input_len: 0x0074fef1
    	output: 0x01000000
    	output_len: 0x00fa63d0
    	kernel_total_size: 0x0107c000
    	needed_size: 0x0107c000
    
    Dump from extract_kernel after patch:
    	early console in extract_kernel
    	input_data: 0x0190d098 <--- this is at output + init_size - _end
    	input_len: 0x0074fef1
    	output: 0x01000000
    	output_len: 0x00fa63d0
    	kernel_total_size: 0x0107c000
    	needed_size: 0x0107c000
    
    Fixes: 974f221c ("x86/boot: Move compressed kernel to the end of the decompression buffer")
    Signed-off-by: default avatarArvind Sankar <nivedita@alum.mit.edu>
    Signed-off-by: default avatarBorislav Petkov <bp@suse.de>
    Link: https://lkml.kernel.org/r/20200207214926.3564079-1-nivedita@alum.mit.edu
    5214028d
head_64.S 19.8 KB