Skip to content

Commit e602336

Browse files
Junjie MaoKAGA-KOKO
Junjie Mao
authored andcommitted
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: 82fa963 ("x86, kaslr: Select random position from e820 maps") Reported-by: Fengguang Wu <[email protected]> Signed-off-by: Junjie Mao <[email protected]> Signed-off-by: Kees Cook <[email protected]> Cc: Josh Triplett <[email protected]> Cc: Matt Fleming <[email protected]> Cc: Ard Biesheuvel <[email protected]> Cc: Vivek Goyal <[email protected]> Cc: Andi Kleen <[email protected]> Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Thomas Gleixner <[email protected]>
1 parent 4750a0d commit e602336

File tree

6 files changed

+57
-9
lines changed

6 files changed

+57
-9
lines changed

arch/x86/boot/compressed/Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,10 @@ suffix-$(CONFIG_KERNEL_XZ) := xz
7676
suffix-$(CONFIG_KERNEL_LZO) := lzo
7777
suffix-$(CONFIG_KERNEL_LZ4) := lz4
7878

79+
RUN_SIZE = $(shell objdump -h vmlinux | \
80+
perl $(srctree)/arch/x86/tools/calc_run_size.pl)
7981
quiet_cmd_mkpiggy = MKPIGGY $@
80-
cmd_mkpiggy = $(obj)/mkpiggy $< > $@ || ( rm -f $@ ; false )
82+
cmd_mkpiggy = $(obj)/mkpiggy $< $(RUN_SIZE) > $@ || ( rm -f $@ ; false )
8183

8284
targets += piggy.S
8385
$(obj)/piggy.S: $(obj)/vmlinux.bin.$(suffix-y) $(obj)/mkpiggy FORCE

arch/x86/boot/compressed/head_32.S

+3-2
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,8 @@ relocated:
207207
* Do the decompression, and jump to the new kernel..
208208
*/
209209
/* push arguments for decompress_kernel: */
210-
pushl $z_output_len /* decompressed length */
210+
pushl $z_run_size /* size of kernel with .bss and .brk */
211+
pushl $z_output_len /* decompressed length, end of relocs */
211212
leal z_extract_offset_negative(%ebx), %ebp
212213
pushl %ebp /* output address */
213214
pushl $z_input_len /* input_len */
@@ -217,7 +218,7 @@ relocated:
217218
pushl %eax /* heap area */
218219
pushl %esi /* real mode pointer */
219220
call decompress_kernel /* returns kernel location in %eax */
220-
addl $24, %esp
221+
addl $28, %esp
221222

222223
/*
223224
* Jump to the decompressed kernel.

arch/x86/boot/compressed/head_64.S

+4-1
Original file line numberDiff line numberDiff line change
@@ -402,13 +402,16 @@ relocated:
402402
* Do the decompression, and jump to the new kernel..
403403
*/
404404
pushq %rsi /* Save the real mode argument */
405+
movq $z_run_size, %r9 /* size of kernel with .bss and .brk */
406+
pushq %r9
405407
movq %rsi, %rdi /* real mode address */
406408
leaq boot_heap(%rip), %rsi /* malloc area for uncompression */
407409
leaq input_data(%rip), %rdx /* input_data */
408410
movl $z_input_len, %ecx /* input_len */
409411
movq %rbp, %r8 /* output target address */
410-
movq $z_output_len, %r9 /* decompressed length */
412+
movq $z_output_len, %r9 /* decompressed length, end of relocs */
411413
call decompress_kernel /* returns kernel location in %rax */
414+
popq %r9
412415
popq %rsi
413416

414417
/*

arch/x86/boot/compressed/misc.c

+10-3
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
358358
unsigned char *input_data,
359359
unsigned long input_len,
360360
unsigned char *output,
361-
unsigned long output_len)
361+
unsigned long output_len,
362+
unsigned long run_size)
362363
{
363364
real_mode = rmode;
364365

@@ -381,8 +382,14 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
381382
free_mem_ptr = heap; /* Heap */
382383
free_mem_end_ptr = heap + BOOT_HEAP_SIZE;
383384

384-
output = choose_kernel_location(input_data, input_len,
385-
output, output_len);
385+
/*
386+
* The memory hole needed for the kernel is the larger of either
387+
* the entire decompressed kernel plus relocation table, or the
388+
* entire decompressed kernel plus .bss and .brk sections.
389+
*/
390+
output = choose_kernel_location(input_data, input_len, output,
391+
output_len > run_size ? output_len
392+
: run_size);
386393

387394
/* Validate memory location choices. */
388395
if ((unsigned long)output & (MIN_KERNEL_ALIGN - 1))

arch/x86/boot/compressed/mkpiggy.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,13 @@ int main(int argc, char *argv[])
3636
uint32_t olen;
3737
long ilen;
3838
unsigned long offs;
39+
unsigned long run_size;
3940
FILE *f = NULL;
4041
int retval = 1;
4142

42-
if (argc < 2) {
43-
fprintf(stderr, "Usage: %s compressed_file\n", argv[0]);
43+
if (argc < 3) {
44+
fprintf(stderr, "Usage: %s compressed_file run_size\n",
45+
argv[0]);
4446
goto bail;
4547
}
4648

@@ -74,6 +76,7 @@ int main(int argc, char *argv[])
7476
offs += olen >> 12; /* Add 8 bytes for each 32K block */
7577
offs += 64*1024 + 128; /* Add 64K + 128 bytes slack */
7678
offs = (offs+4095) & ~4095; /* Round to a 4K boundary */
79+
run_size = atoi(argv[2]);
7780

7881
printf(".section \".rodata..compressed\",\"a\",@progbits\n");
7982
printf(".globl z_input_len\n");
@@ -85,6 +88,8 @@ int main(int argc, char *argv[])
8588
/* z_extract_offset_negative allows simplification of head_32.S */
8689
printf(".globl z_extract_offset_negative\n");
8790
printf("z_extract_offset_negative = -0x%lx\n", offs);
91+
printf(".globl z_run_size\n");
92+
printf("z_run_size = %lu\n", run_size);
8893

8994
printf(".globl input_data, input_data_end\n");
9095
printf("input_data:\n");

arch/x86/tools/calc_run_size.pl

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/usr/bin/perl
2+
#
3+
# Calculate the amount of space needed to run the kernel, including room for
4+
# the .bss and .brk sections.
5+
#
6+
# Usage:
7+
# objdump -h a.out | perl calc_run_size.pl
8+
use strict;
9+
10+
my $mem_size = 0;
11+
my $file_offset = 0;
12+
13+
my $sections=" *[0-9]+ \.(?:bss|brk) +";
14+
while (<>) {
15+
if (/^$sections([0-9a-f]+) +(?:[0-9a-f]+ +){2}([0-9a-f]+)/) {
16+
my $size = hex($1);
17+
my $offset = hex($2);
18+
$mem_size += $size;
19+
if ($file_offset == 0) {
20+
$file_offset = $offset;
21+
} elsif ($file_offset != $offset) {
22+
die ".bss and .brk lack common file offset\n";
23+
}
24+
}
25+
}
26+
27+
if ($file_offset == 0) {
28+
die "Never found .bss or .brk file offset\n";
29+
}
30+
printf("%d\n", $mem_size + $file_offset);

0 commit comments

Comments
 (0)