diff options
| author | Yinghai Lu <yinghai@kernel.org> | 2015-09-09 18:39:12 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-10 16:29:01 -0400 |
| commit | 2d3862d26e67a59340ba1cf1748196c76c5787de (patch) | |
| tree | e3b1dd9157a0c745bcd8147cad82bf021a1cec72 | |
| parent | e852d82a5b55b44ce8be89078d0dfbddbeae3211 (diff) | |
lib/decompressors: use real out buf size for gunzip with kernel
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: 1431574a1c4 (lib/decompressors: fix "no limit" output buffer length)
Signed-off-by: Yinghai Lu <yinghai@kernel.org>
Cc: Alexandre Courbot <acourbot@nvidia.com>
Cc: Jon Medhurst <tixy@linaro.org>
Cc: Stephen Warren <swarren@wwwdotorg.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | arch/arm/boot/compressed/decompress.c | 2 | ||||
| -rw-r--r-- | arch/h8300/boot/compressed/misc.c | 2 | ||||
| -rw-r--r-- | arch/m32r/boot/compressed/misc.c | 3 | ||||
| -rw-r--r-- | arch/mips/boot/compressed/decompress.c | 4 | ||||
| -rw-r--r-- | arch/s390/boot/compressed/misc.c | 2 | ||||
| -rw-r--r-- | arch/sh/boot/compressed/misc.c | 2 | ||||
| -rw-r--r-- | arch/unicore32/boot/compressed/misc.c | 4 | ||||
| -rw-r--r-- | arch/x86/boot/compressed/misc.c | 3 | ||||
| -rw-r--r-- | lib/decompress_bunzip2.c | 6 | ||||
| -rw-r--r-- | lib/decompress_inflate.c | 31 | ||||
| -rw-r--r-- | lib/decompress_unlz4.c | 6 | ||||
| -rw-r--r-- | lib/decompress_unlzma.c | 7 | ||||
| -rw-r--r-- | lib/decompress_unlzo.c | 13 | ||||
| -rw-r--r-- | lib/decompress_unxz.c | 12 |
14 files changed, 70 insertions, 27 deletions
diff --git a/arch/arm/boot/compressed/decompress.c b/arch/arm/boot/compressed/decompress.c index bd245d34952d..a0765e7ed6c7 100644 --- a/arch/arm/boot/compressed/decompress.c +++ b/arch/arm/boot/compressed/decompress.c | |||
| @@ -57,5 +57,5 @@ extern char * strstr(const char * s1, const char *s2); | |||
| 57 | 57 | ||
| 58 | int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)) | 58 | int do_decompress(u8 *input, int len, u8 *output, void (*error)(char *x)) |
| 59 | { | 59 | { |
| 60 | return decompress(input, len, NULL, NULL, output, NULL, error); | 60 | return __decompress(input, len, NULL, NULL, output, 0, NULL, error); |
| 61 | } | 61 | } |
diff --git a/arch/h8300/boot/compressed/misc.c b/arch/h8300/boot/compressed/misc.c index 704274127c07..c4f2cfcb117b 100644 --- a/arch/h8300/boot/compressed/misc.c +++ b/arch/h8300/boot/compressed/misc.c | |||
| @@ -70,5 +70,5 @@ void decompress_kernel(void) | |||
| 70 | free_mem_ptr = (unsigned long)&_end; | 70 | free_mem_ptr = (unsigned long)&_end; |
| 71 | free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; | 71 | free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; |
| 72 | 72 | ||
| 73 | decompress(input_data, input_len, NULL, NULL, output, NULL, error); | 73 | __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); |
| 74 | } | 74 | } |
diff --git a/arch/m32r/boot/compressed/misc.c b/arch/m32r/boot/compressed/misc.c index 28a09529f206..3a7692745868 100644 --- a/arch/m32r/boot/compressed/misc.c +++ b/arch/m32r/boot/compressed/misc.c | |||
| @@ -86,6 +86,7 @@ decompress_kernel(int mmu_on, unsigned char *zimage_data, | |||
| 86 | free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE; | 86 | free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE; |
| 87 | 87 | ||
| 88 | puts("\nDecompressing Linux... "); | 88 | puts("\nDecompressing Linux... "); |
| 89 | decompress(input_data, input_len, NULL, NULL, output_data, NULL, error); | 89 | __decompress(input_data, input_len, NULL, NULL, output_data, 0, |
| 90 | NULL, error); | ||
| 90 | puts("done.\nBooting the kernel.\n"); | 91 | puts("done.\nBooting the kernel.\n"); |
| 91 | } | 92 | } |
diff --git a/arch/mips/boot/compressed/decompress.c b/arch/mips/boot/compressed/decompress.c index 54831069a206..080cd53bac36 100644 --- a/arch/mips/boot/compressed/decompress.c +++ b/arch/mips/boot/compressed/decompress.c | |||
| @@ -111,8 +111,8 @@ void decompress_kernel(unsigned long boot_heap_start) | |||
| 111 | puts("\n"); | 111 | puts("\n"); |
| 112 | 112 | ||
| 113 | /* Decompress the kernel with according algorithm */ | 113 | /* Decompress the kernel with according algorithm */ |
| 114 | decompress((char *)zimage_start, zimage_size, 0, 0, | 114 | __decompress((char *)zimage_start, zimage_size, 0, 0, |
| 115 | (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, error); | 115 | (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, 0, error); |
| 116 | 116 | ||
| 117 | /* FIXME: should we flush cache here? */ | 117 | /* FIXME: should we flush cache here? */ |
| 118 | puts("Now, booting the kernel...\n"); | 118 | puts("Now, booting the kernel...\n"); |
diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index 42506b371b74..4da604ebf6fd 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c | |||
| @@ -167,7 +167,7 @@ unsigned long decompress_kernel(void) | |||
| 167 | #endif | 167 | #endif |
| 168 | 168 | ||
| 169 | puts("Uncompressing Linux... "); | 169 | puts("Uncompressing Linux... "); |
| 170 | decompress(input_data, input_len, NULL, NULL, output, NULL, error); | 170 | __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); |
| 171 | puts("Ok, booting the kernel.\n"); | 171 | puts("Ok, booting the kernel.\n"); |
| 172 | return (unsigned long) output; | 172 | return (unsigned long) output; |
| 173 | } | 173 | } |
diff --git a/arch/sh/boot/compressed/misc.c b/arch/sh/boot/compressed/misc.c index 95470a472d2c..208a9753ab38 100644 --- a/arch/sh/boot/compressed/misc.c +++ b/arch/sh/boot/compressed/misc.c | |||
| @@ -132,7 +132,7 @@ void decompress_kernel(void) | |||
| 132 | 132 | ||
| 133 | puts("Uncompressing Linux... "); | 133 | puts("Uncompressing Linux... "); |
| 134 | cache_control(CACHE_ENABLE); | 134 | cache_control(CACHE_ENABLE); |
| 135 | decompress(input_data, input_len, NULL, NULL, output, NULL, error); | 135 | __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error); |
| 136 | cache_control(CACHE_DISABLE); | 136 | cache_control(CACHE_DISABLE); |
| 137 | puts("Ok, booting the kernel.\n"); | 137 | puts("Ok, booting the kernel.\n"); |
| 138 | } | 138 | } |
diff --git a/arch/unicore32/boot/compressed/misc.c b/arch/unicore32/boot/compressed/misc.c index 176d5bda3559..5c65dfee278c 100644 --- a/arch/unicore32/boot/compressed/misc.c +++ b/arch/unicore32/boot/compressed/misc.c | |||
| @@ -119,8 +119,8 @@ unsigned long decompress_kernel(unsigned long output_start, | |||
| 119 | output_ptr = get_unaligned_le32(tmp); | 119 | output_ptr = get_unaligned_le32(tmp); |
| 120 | 120 | ||
| 121 | arch_decomp_puts("Uncompressing Linux..."); | 121 | arch_decomp_puts("Uncompressing Linux..."); |
| 122 | decompress(input_data, input_data_end - input_data, NULL, NULL, | 122 | __decompress(input_data, input_data_end - input_data, NULL, NULL, |
| 123 | output_data, NULL, error); | 123 | output_data, 0, NULL, error); |
| 124 | arch_decomp_puts(" done, booting the kernel.\n"); | 124 | arch_decomp_puts(" done, booting the kernel.\n"); |
| 125 | return output_ptr; | 125 | return output_ptr; |
| 126 | } | 126 | } |
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c index f63797942bb5..79dac1758e7c 100644 --- a/arch/x86/boot/compressed/misc.c +++ b/arch/x86/boot/compressed/misc.c | |||
| @@ -448,7 +448,8 @@ asmlinkage __visible void *decompress_kernel(void *rmode, memptr heap, | |||
| 448 | #endif | 448 | #endif |
| 449 | 449 | ||
| 450 | debug_putstr("\nDecompressing Linux... "); | 450 | debug_putstr("\nDecompressing Linux... "); |
| 451 | decompress(input_data, input_len, NULL, NULL, output, NULL, error); | 451 | __decompress(input_data, input_len, NULL, NULL, output, output_len, |
| 452 | NULL, error); | ||
| 452 | parse_elf(output); | 453 | parse_elf(output); |
| 453 | /* | 454 | /* |
| 454 | * 32-bit always performs relocations. 64-bit relocations are only | 455 | * 32-bit always performs relocations. 64-bit relocations are only |
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c index 6dd0335ea61b..0234361b24b8 100644 --- a/lib/decompress_bunzip2.c +++ b/lib/decompress_bunzip2.c | |||
| @@ -743,12 +743,12 @@ exit_0: | |||
| 743 | } | 743 | } |
| 744 | 744 | ||
| 745 | #ifdef PREBOOT | 745 | #ifdef PREBOOT |
| 746 | STATIC int INIT decompress(unsigned char *buf, long len, | 746 | STATIC int INIT __decompress(unsigned char *buf, long len, |
| 747 | long (*fill)(void*, unsigned long), | 747 | long (*fill)(void*, unsigned long), |
| 748 | long (*flush)(void*, unsigned long), | 748 | long (*flush)(void*, unsigned long), |
| 749 | unsigned char *outbuf, | 749 | unsigned char *outbuf, long olen, |
| 750 | long *pos, | 750 | long *pos, |
| 751 | void(*error)(char *x)) | 751 | void (*error)(char *x)) |
| 752 | { | 752 | { |
| 753 | return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error); | 753 | return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error); |
| 754 | } | 754 | } |
diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c index d4c7891635ec..555c06bf20da 100644 --- a/lib/decompress_inflate.c +++ b/lib/decompress_inflate.c | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #ifdef STATIC | 1 | #ifdef STATIC |
| 2 | #define PREBOOT | ||
| 2 | /* Pre-boot environment: included */ | 3 | /* Pre-boot environment: included */ |
| 3 | 4 | ||
| 4 | /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots | 5 | /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots |
| @@ -33,23 +34,23 @@ static long INIT nofill(void *buffer, unsigned long len) | |||
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | /* Included from initramfs et al code */ | 36 | /* Included from initramfs et al code */ |
| 36 | STATIC int INIT gunzip(unsigned char *buf, long len, | 37 | STATIC int INIT __gunzip(unsigned char *buf, long len, |
| 37 | long (*fill)(void*, unsigned long), | 38 | long (*fill)(void*, unsigned long), |
| 38 | long (*flush)(void*, unsigned long), | 39 | long (*flush)(void*, unsigned long), |
| 39 | unsigned char *out_buf, | 40 | unsigned char *out_buf, long out_len, |
| 40 | long *pos, | 41 | long *pos, |
| 41 | void(*error)(char *x)) { | 42 | void(*error)(char *x)) { |
| 42 | u8 *zbuf; | 43 | u8 *zbuf; |
| 43 | struct z_stream_s *strm; | 44 | struct z_stream_s *strm; |
| 44 | int rc; | 45 | int rc; |
| 45 | size_t out_len; | ||
| 46 | 46 | ||
| 47 | rc = -1; | 47 | rc = -1; |
| 48 | if (flush) { | 48 | if (flush) { |
| 49 | out_len = 0x8000; /* 32 K */ | 49 | out_len = 0x8000; /* 32 K */ |
| 50 | out_buf = malloc(out_len); | 50 | out_buf = malloc(out_len); |
| 51 | } else { | 51 | } else { |
| 52 | out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ | 52 | if (!out_len) |
| 53 | out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ | ||
| 53 | } | 54 | } |
| 54 | if (!out_buf) { | 55 | if (!out_buf) { |
| 55 | error("Out of memory while allocating output buffer"); | 56 | error("Out of memory while allocating output buffer"); |
| @@ -181,4 +182,24 @@ gunzip_nomem1: | |||
| 181 | return rc; /* returns Z_OK (0) if successful */ | 182 | return rc; /* returns Z_OK (0) if successful */ |
| 182 | } | 183 | } |
| 183 | 184 | ||
| 184 | #define decompress gunzip | 185 | #ifndef PREBOOT |
| 186 | STATIC int INIT gunzip(unsigned char *buf, long len, | ||
| 187 | long (*fill)(void*, unsigned long), | ||
| 188 | long (*flush)(void*, unsigned long), | ||
| 189 | unsigned char *out_buf, | ||
| 190 | long *pos, | ||
| 191 | void (*error)(char *x)) | ||
| 192 | { | ||
| 193 | return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error); | ||
| 194 | } | ||
| 195 | #else | ||
| 196 | STATIC int INIT __decompress(unsigned char *buf, long len, | ||
| 197 | long (*fill)(void*, unsigned long), | ||
| 198 | long (*flush)(void*, unsigned long), | ||
| 199 | unsigned char *out_buf, long out_len, | ||
| 200 | long *pos, | ||
| 201 | void (*error)(char *x)) | ||
| 202 | { | ||
| 203 | return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error); | ||
| 204 | } | ||
| 205 | #endif | ||
diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c index 40f66ebe57b7..036fc882cd72 100644 --- a/lib/decompress_unlz4.c +++ b/lib/decompress_unlz4.c | |||
| @@ -196,12 +196,12 @@ exit_0: | |||
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | #ifdef PREBOOT | 198 | #ifdef PREBOOT |
| 199 | STATIC int INIT decompress(unsigned char *buf, long in_len, | 199 | STATIC int INIT __decompress(unsigned char *buf, long in_len, |
| 200 | long (*fill)(void*, unsigned long), | 200 | long (*fill)(void*, unsigned long), |
| 201 | long (*flush)(void*, unsigned long), | 201 | long (*flush)(void*, unsigned long), |
| 202 | unsigned char *output, | 202 | unsigned char *output, long out_len, |
| 203 | long *posp, | 203 | long *posp, |
| 204 | void(*error)(char *x) | 204 | void (*error)(char *x) |
| 205 | ) | 205 | ) |
| 206 | { | 206 | { |
| 207 | return unlz4(buf, in_len - 4, fill, flush, output, posp, error); | 207 | return unlz4(buf, in_len - 4, fill, flush, output, posp, error); |
diff --git a/lib/decompress_unlzma.c b/lib/decompress_unlzma.c index 0be83af62b88..decb64629c14 100644 --- a/lib/decompress_unlzma.c +++ b/lib/decompress_unlzma.c | |||
| @@ -667,13 +667,12 @@ exit_0: | |||
| 667 | } | 667 | } |
| 668 | 668 | ||
| 669 | #ifdef PREBOOT | 669 | #ifdef PREBOOT |
| 670 | STATIC int INIT decompress(unsigned char *buf, long in_len, | 670 | STATIC int INIT __decompress(unsigned char *buf, long in_len, |
| 671 | long (*fill)(void*, unsigned long), | 671 | long (*fill)(void*, unsigned long), |
| 672 | long (*flush)(void*, unsigned long), | 672 | long (*flush)(void*, unsigned long), |
| 673 | unsigned char *output, | 673 | unsigned char *output, long out_len, |
| 674 | long *posp, | 674 | long *posp, |
| 675 | void(*error)(char *x) | 675 | void (*error)(char *x)) |
| 676 | ) | ||
| 677 | { | 676 | { |
| 678 | return unlzma(buf, in_len - 4, fill, flush, output, posp, error); | 677 | return unlzma(buf, in_len - 4, fill, flush, output, posp, error); |
| 679 | } | 678 | } |
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c index b94a31bdd87d..f4c158e3a022 100644 --- a/lib/decompress_unlzo.c +++ b/lib/decompress_unlzo.c | |||
| @@ -31,6 +31,7 @@ | |||
| 31 | */ | 31 | */ |
| 32 | 32 | ||
| 33 | #ifdef STATIC | 33 | #ifdef STATIC |
| 34 | #define PREBOOT | ||
| 34 | #include "lzo/lzo1x_decompress_safe.c" | 35 | #include "lzo/lzo1x_decompress_safe.c" |
| 35 | #else | 36 | #else |
| 36 | #include <linux/decompress/unlzo.h> | 37 | #include <linux/decompress/unlzo.h> |
| @@ -287,4 +288,14 @@ exit: | |||
| 287 | return ret; | 288 | return ret; |
| 288 | } | 289 | } |
| 289 | 290 | ||
| 290 | #define decompress unlzo | 291 | #ifdef PREBOOT |
| 292 | STATIC int INIT __decompress(unsigned char *buf, long len, | ||
| 293 | long (*fill)(void*, unsigned long), | ||
| 294 | long (*flush)(void*, unsigned long), | ||
| 295 | unsigned char *out_buf, long olen, | ||
| 296 | long *pos, | ||
| 297 | void (*error)(char *x)) | ||
| 298 | { | ||
| 299 | return unlzo(buf, len, fill, flush, out_buf, pos, error); | ||
| 300 | } | ||
| 301 | #endif | ||
diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c index b07a78340e9d..25d59a95bd66 100644 --- a/lib/decompress_unxz.c +++ b/lib/decompress_unxz.c | |||
| @@ -394,4 +394,14 @@ error_alloc_state: | |||
| 394 | * This macro is used by architecture-specific files to decompress | 394 | * This macro is used by architecture-specific files to decompress |
| 395 | * the kernel image. | 395 | * the kernel image. |
| 396 | */ | 396 | */ |
| 397 | #define decompress unxz | 397 | #ifdef XZ_PREBOOT |
| 398 | STATIC int INIT __decompress(unsigned char *buf, long len, | ||
| 399 | long (*fill)(void*, unsigned long), | ||
| 400 | long (*flush)(void*, unsigned long), | ||
| 401 | unsigned char *out_buf, long olen, | ||
| 402 | long *pos, | ||
| 403 | void (*error)(char *x)) | ||
| 404 | { | ||
| 405 | return unxz(buf, len, fill, flush, out_buf, pos, error); | ||
| 406 | } | ||
| 407 | #endif | ||
