x86, kaslr: Prevent .bss from overlaping initrd
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>
Cc: stable@vger.kernel.org
Link: http://lkml.kernel.org/r/1414762838-13067-1-git-send-email-eternal.n08@gmail.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
parent
4750a0d112
commit
e6023367d7
|
@ -76,8 +76,10 @@ suffix-$(CONFIG_KERNEL_XZ) := xz
|
||||||
suffix-$(CONFIG_KERNEL_LZO) := lzo
|
suffix-$(CONFIG_KERNEL_LZO) := lzo
|
||||||
suffix-$(CONFIG_KERNEL_LZ4) := lz4
|
suffix-$(CONFIG_KERNEL_LZ4) := lz4
|
||||||
|
|
||||||
|
RUN_SIZE = $(shell objdump -h vmlinux | \
|
||||||
|
perl $(srctree)/arch/x86/tools/calc_run_size.pl)
|
||||||
quiet_cmd_mkpiggy = MKPIGGY $@
|
quiet_cmd_mkpiggy = MKPIGGY $@
|
||||||
cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
|
cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
|
||||||
|
|
||||||
targets += piggy.S
|
targets += piggy.S
|
||||||
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
|
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE
|
||||||
|
|
|
@ -207,7 +207,8 @@ relocated:
|
||||||
* Do the decompression, and jump to the new kernel..
|
* Do the decompression, and jump to the new kernel..
|
||||||
*/
|
*/
|
||||||
/* push arguments for decompress_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
|
leal z_extract_offset_negative(%ebx), %ebp
|
||||||
pushl %ebp /* output address */
|
pushl %ebp /* output address */
|
||||||
pushl $z_input_len /* input_len */
|
pushl $z_input_len /* input_len */
|
||||||
|
@ -217,7 +218,7 @@ relocated:
|
||||||
pushl %eax /* heap area */
|
pushl %eax /* heap area */
|
||||||
pushl %esi /* real mode pointer */
|
pushl %esi /* real mode pointer */
|
||||||
call decompress_kernel /* returns kernel location in %eax */
|
call decompress_kernel /* returns kernel location in %eax */
|
||||||
addl $24, %esp
|
addl $28, %esp
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Jump to the decompressed kernel.
|
* Jump to the decompressed kernel.
|
||||||
|
|
|
@ -402,13 +402,16 @@ relocated:
|
||||||
* Do the decompression, and jump to the new kernel..
|
* Do the decompression, and jump to the new kernel..
|
||||||
*/
|
*/
|
||||||
pushq %rsi /* Save the real mode argument */
|
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 */
|
movq %rsi, %rdi /* real mode address */
|
||||||
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
|
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
|
||||||
leaq input_data(%rip), %rdx /* input_data */
|
leaq input_data(%rip), %rdx /* input_data */
|
||||||
movl $z_input_len, %ecx /* input_len */
|
movl $z_input_len, %ecx /* input_len */
|
||||||
movq %rbp, %r8 /* output target address */
|
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 */
|
call decompress_kernel /* returns kernel location in %rax */
|
||||||
|
popq %r9
|
||||||
popq %rsi
|
popq %rsi
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -358,7 +358,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
|
||||||
unsigned char *input_data,
|
unsigned char *input_data,
|
||||||
unsigned long input_len,
|
unsigned long input_len,
|
||||||
unsigned char *output,
|
unsigned char *output,
|
||||||
unsigned long output_len)
|
unsigned long output_len,
|
||||||
|
unsigned long run_size)
|
||||||
{
|
{
|
||||||
real_mode = rmode;
|
real_mode = rmode;
|
||||||
|
|
||||||
|
@ -381,8 +382,14 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
|
||||||
free_mem_ptr = heap; /* Heap */
|
free_mem_ptr = heap; /* Heap */
|
||||||
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
|
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. */
|
/* Validate memory location choices. */
|
||||||
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
|
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))
|
||||||
|
|
|
@ -36,11 +36,13 @@ int main(int argc, char *argv[])
|
||||||
uint32_t olen;
|
uint32_t olen;
|
||||||
long ilen;
|
long ilen;
|
||||||
unsigned long offs;
|
unsigned long offs;
|
||||||
|
unsigned long run_size;
|
||||||
FILE *f = NULL;
|
FILE *f = NULL;
|
||||||
int retval = 1;
|
int retval = 1;
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 3) {
|
||||||
fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
|
fprintf(stderr, "Usage: %s compressed_file run_size\n",
|
||||||
|
argv[0]);
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +76,7 @@ int main(int argc, char *argv[])
|
||||||
offs += olen >> 12; /* Add 8 bytes for each 32K block */
|
offs += olen >> 12; /* Add 8 bytes for each 32K block */
|
||||||
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
|
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
|
||||||
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
|
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
|
||||||
|
run_size = atoi(argv[2]);
|
||||||
|
|
||||||
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
|
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
|
||||||
printf(".globl z_input_len\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 */
|
/* z_extract_offset_negative allows simplification of head_32.S */
|
||||||
printf(".globl z_extract_offset_negative\n");
|
printf(".globl z_extract_offset_negative\n");
|
||||||
printf("z_extract_offset_negative = -0x%lx\n", offs);
|
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(".globl input_data, input_data_end\n");
|
||||||
printf("input_data:\n");
|
printf("input_data:\n");
|
||||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue