Skip to content

Commit

Permalink
lib/decompressors: use real out buf size for gunzip with kernel
Browse files Browse the repository at this point in the history
When loading x86 64bit kernel above 4GiB with patched grub2, got kernel
gunzip error.

| early console in decompress_kernel
| decompress_kernel:
|       input: [0x807f2143b4-0x807ff61aee]
|      output: [0x807cc00000-0x807f3ea29b] 0x027ea29c: output_len
| boot via startup_64
| KASLR using RDTSC...
|  new output: [0x46fe000000-0x470138cfff] 0x0338d000: output_run_size
|  decompress: [0x46fe000000-0x47007ea29b] <=== [0x807f2143b4-0x807ff61aee]
|
| Decompressing Linux... gz...
|
| uncompression error
|
| -- System halted

the new buffer is at 0x46fe000000ULL, decompressor_gzip is using
0xffffffb901ffffff as out_len.  gunzip in lib/zlib_inflate/inflate.c cap
that len to 0x01ffffff and decompress fails later.

We could hit this problem with crashkernel booting that uses kexec loading
kernel above 4GiB.

We have decompress_* support:
    1. inbuf[]/outbuf[] for kernel preboot.
    2. inbuf[]/flush() for initramfs
    3. fill()/flush() for initrd.
This bug only affect kernel preboot path that use outbuf[].

Add __decompress and take real out_buf_len for gunzip instead of guessing
wrong buf size.

Fixes: 1431574 (lib/decompressors: fix "no limit" output buffer length)
Signed-off-by: Yinghai Lu <[email protected]>
Cc: Alexandre Courbot <[email protected]>
Cc: Jon Medhurst <[email protected]>
Cc: Stephen Warren <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Yinghai Lu authored and torvalds committed Sep 10, 2015
1 parent e852d82 commit 2d3862d
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 27 deletions.
2 changes: 1 addition & 1 deletion arch/arm/boot/compressed/decompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ extern char * strstr(const char * s1, const char *s2);

int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x))
{
return decompress(input, len, NULL, NULL, output, NULL, error);
return __decompress(input, len, NULL, NULL, output, 0, NULL, error);
}
2 changes: 1 addition & 1 deletion arch/h8300/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ void decompress_kernel(void)
free_mem_ptr = (unsigned long)&_end;
free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;

decompress(input_data, input_len, NULL, NULL, output, NULL, error);
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
}
3 changes: 2 additions & 1 deletion arch/m32r/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ decompress_kernel(int mmu_on, unsigned char *zimage_data,
free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE;

puts("\nDecompressing Linux... ");
decompress(input_data, input_len, NULL, NULL, output_data, NULL, error);
__decompress(input_data, input_len, NULL, NULL, output_data, 0,
NULL, error);
puts("done.\nBooting the kernel.\n");
}
4 changes: 2 additions & 2 deletions arch/mips/boot/compressed/decompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ void decompress_kernel(unsigned long boot_heap_start)
puts("\n");

/* Decompress the kernel with according algorithm */
decompress((char *)zimage_start, zimage_size, 0, 0,
(void *)VMLINUX_LOAD_ADDRESS_ULL, 0, error);
__decompress((char *)zimage_start, zimage_size, 0, 0,
(void *)VMLINUX_LOAD_ADDRESS_ULL, 0, 0, error);

/* FIXME: should we flush cache here? */
puts("Now, booting the kernel...\n");
Expand Down
2 changes: 1 addition & 1 deletion arch/s390/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ unsigned long decompress_kernel(void)
#endif

puts("Uncompressing Linux... ");
decompress(input_data, input_len, NULL, NULL, output, NULL, error);
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
puts("Ok, booting the kernel.\n");
return (unsigned long) output;
}
Expand Down
2 changes: 1 addition & 1 deletion arch/sh/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void decompress_kernel(void)

puts("Uncompressing Linux... ");
cache_control(CACHE_ENABLE);
decompress(input_data, input_len, NULL, NULL, output, NULL, error);
__decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
cache_control(CACHE_DISABLE);
puts("Ok, booting the kernel.\n");
}
4 changes: 2 additions & 2 deletions arch/unicore32/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ unsigned long decompress_kernel(unsigned long output_start,
output_ptr = get_unaligned_le32(tmp);

arch_decomp_puts("Uncompressing Linux...");
decompress(input_data, input_data_end - input_data, NULL, NULL,
output_data, NULL, error);
__decompress(input_data, input_data_end - input_data, NULL, NULL,
output_data, 0, NULL, error);
arch_decomp_puts(" done, booting the kernel.\n");
return output_ptr;
}
3 changes: 2 additions & 1 deletion arch/x86/boot/compressed/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap,
#endif

debug_putstr("\nDecompressing Linux... ");
decompress(input_data, input_len, NULL, NULL, output, NULL, error);
__decompress(input_data, input_len, NULL, NULL, output, output_len,
NULL, error);
parse_elf(output);
/*
* 32-bit always performs relocations. 64-bit relocations are only
Expand Down
6 changes: 3 additions & 3 deletions lib/decompress_bunzip2.c
Original file line number Diff line number Diff line change
Expand Up @@ -743,12 +743,12 @@ STATIC int INIT bunzip2(unsigned char *buf, long len,
}

#ifdef PREBOOT
STATIC int INIT decompress(unsigned char *buf, long len,
STATIC int INIT __decompress(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *outbuf,
unsigned char *outbuf, long olen,
long *pos,
void(*error)(char *x))
void (*error)(char *x))
{
return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error);
}
Expand Down
31 changes: 26 additions & 5 deletions lib/decompress_inflate.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#ifdef STATIC
#define PREBOOT
/* Pre-boot environment: included */

/* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
Expand Down Expand Up @@ -33,23 +34,23 @@ static long INIT nofill(void *buffer, unsigned long len)
}

/* Included from initramfs et al code */
STATIC int INIT gunzip(unsigned char *buf, long len,
STATIC int INIT __gunzip(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf,
unsigned char *out_buf, long out_len,
long *pos,
void(*error)(char *x)) {
u8 *zbuf;
struct z_stream_s *strm;
int rc;
size_t out_len;

rc = -1;
if (flush) {
out_len = 0x8000; /* 32 K */
out_buf = malloc(out_len);
} else {
out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
if (!out_len)
out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
}
if (!out_buf) {
error("Out of memory while allocating output buffer");
Expand Down Expand Up @@ -181,4 +182,24 @@ STATIC int INIT gunzip(unsigned char *buf, long len,
return rc; /* returns Z_OK (0) if successful */
}

#define decompress gunzip
#ifndef PREBOOT
STATIC int INIT gunzip(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf,
long *pos,
void (*error)(char *x))
{
return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
}
#else
STATIC int INIT __decompress(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf, long out_len,
long *pos,
void (*error)(char *x))
{
return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
}
#endif
6 changes: 3 additions & 3 deletions lib/decompress_unlz4.c
Original file line number Diff line number Diff line change
Expand Up @@ -196,12 +196,12 @@ STATIC inline int INIT unlz4(u8 *input, long in_len,
}

#ifdef PREBOOT
STATIC int INIT decompress(unsigned char *buf, long in_len,
STATIC int INIT __decompress(unsigned char *buf, long in_len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *output,
unsigned char *output, long out_len,
long *posp,
void(*error)(char *x)
void (*error)(char *x)
)
{
return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
Expand Down
7 changes: 3 additions & 4 deletions lib/decompress_unlzma.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,13 +667,12 @@ STATIC inline int INIT unlzma(unsigned char *buf, long in_len,
}

#ifdef PREBOOT
STATIC int INIT decompress(unsigned char *buf, long in_len,
STATIC int INIT __decompress(unsigned char *buf, long in_len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *output,
unsigned char *output, long out_len,
long *posp,
void(*error)(char *x)
)
void (*error)(char *x))
{
return unlzma(buf, in_len - 4, fill, flush, output, posp, error);
}
Expand Down
13 changes: 12 additions & 1 deletion lib/decompress_unlzo.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
*/

#ifdef STATIC
#define PREBOOT
#include "lzo/lzo1x_decompress_safe.c"
#else
#include <linux/decompress/unlzo.h>
Expand Down Expand Up @@ -287,4 +288,14 @@ STATIC int INIT unlzo(u8 *input, long in_len,
return ret;
}

#define decompress unlzo
#ifdef PREBOOT
STATIC int INIT __decompress(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf, long olen,
long *pos,
void (*error)(char *x))
{
return unlzo(buf, len, fill, flush, out_buf, pos, error);
}
#endif
12 changes: 11 additions & 1 deletion lib/decompress_unxz.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,4 +394,14 @@ STATIC int INIT unxz(unsigned char *in, long in_size,
* This macro is used by architecture-specific files to decompress
* the kernel image.
*/
#define decompress unxz
#ifdef XZ_PREBOOT
STATIC int INIT __decompress(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf, long olen,
long *pos,
void (*error)(char *x))
{
return unxz(buf, len, fill, flush, out_buf, pos, error);
}
#endif

0 comments on commit 2d3862d

Please sign in to comment.