Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Yinghai Lu and committed by
Linus Torvalds
2d3862d2 e852d82a

+70 -27
+1 -1
arch/arm/boot/compressed/decompress.c
··· 57 57 58 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 }
+1 -1
arch/h8300/boot/compressed/misc.c
··· 70 70 free_mem_ptr = (unsigned long)&_end; 71 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 }
+2 -1
arch/m32r/boot/compressed/misc.c
··· 86 86 free_mem_end_ptr = free_mem_ptr + BOOT_HEAP_SIZE; 87 87 88 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 91 puts("done.\nBooting the kernel.\n"); 91 92 }
+2 -2
arch/mips/boot/compressed/decompress.c
··· 111 111 puts("\n"); 112 112 113 113 /* Decompress the kernel with according algorithm */ 114 - decompress((char *)zimage_start, zimage_size, 0, 0, 115 - (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, error); 114 + __decompress((char *)zimage_start, zimage_size, 0, 0, 115 + (void *)VMLINUX_LOAD_ADDRESS_ULL, 0, 0, error); 116 116 117 117 /* FIXME: should we flush cache here? */ 118 118 puts("Now, booting the kernel...\n");
+1 -1
arch/s390/boot/compressed/misc.c
··· 167 167 #endif 168 168 169 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 171 puts("Ok, booting the kernel.\n"); 172 172 return (unsigned long) output; 173 173 }
+1 -1
arch/sh/boot/compressed/misc.c
··· 132 132 133 133 puts("Uncompressing Linux... "); 134 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 136 cache_control(CACHE_DISABLE); 137 137 puts("Ok, booting the kernel.\n"); 138 138 }
+2 -2
arch/unicore32/boot/compressed/misc.c
··· 119 119 output_ptr = get_unaligned_le32(tmp); 120 120 121 121 arch_decomp_puts("Uncompressing Linux..."); 122 - decompress(input_data, input_data_end - input_data, NULL, NULL, 123 - output_data, NULL, error); 122 + __decompress(input_data, input_data_end - input_data, NULL, NULL, 123 + output_data, 0, NULL, error); 124 124 arch_decomp_puts(" done, booting the kernel.\n"); 125 125 return output_ptr; 126 126 }
+2 -1
arch/x86/boot/compressed/misc.c
··· 448 448 #endif 449 449 450 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 453 parse_elf(output); 453 454 /* 454 455 * 32-bit always performs relocations. 64-bit relocations are only
+3 -3
lib/decompress_bunzip2.c
··· 743 743 } 744 744 745 745 #ifdef PREBOOT 746 - STATIC int INIT decompress(unsigned char *buf, long len, 746 + STATIC int INIT __decompress(unsigned char *buf, long len, 747 747 long (*fill)(void*, unsigned long), 748 748 long (*flush)(void*, unsigned long), 749 - unsigned char *outbuf, 749 + unsigned char *outbuf, long olen, 750 750 long *pos, 751 - void(*error)(char *x)) 751 + void (*error)(char *x)) 752 752 { 753 753 return bunzip2(buf, len - 4, fill, flush, outbuf, pos, error); 754 754 }
+26 -5
lib/decompress_inflate.c
··· 1 1 #ifdef STATIC 2 + #define PREBOOT 2 3 /* Pre-boot environment: included */ 3 4 4 5 /* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots ··· 34 33 } 35 34 36 35 /* Included from initramfs et al code */ 37 - STATIC int INIT gunzip(unsigned char *buf, long len, 36 + STATIC int INIT __gunzip(unsigned char *buf, long len, 38 37 long (*fill)(void*, unsigned long), 39 38 long (*flush)(void*, unsigned long), 40 - unsigned char *out_buf, 39 + unsigned char *out_buf, long out_len, 41 40 long *pos, 42 41 void(*error)(char *x)) { 43 42 u8 *zbuf; 44 43 struct z_stream_s *strm; 45 44 int rc; 46 - size_t out_len; 47 45 48 46 rc = -1; 49 47 if (flush) { 50 48 out_len = 0x8000; /* 32 K */ 51 49 out_buf = malloc(out_len); 52 50 } else { 53 - out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ 51 + if (!out_len) 52 + out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */ 54 53 } 55 54 if (!out_buf) { 56 55 error("Out of memory while allocating output buffer"); ··· 182 181 return rc; /* returns Z_OK (0) if successful */ 183 182 } 184 183 185 - #define decompress gunzip 184 + #ifndef PREBOOT 185 + STATIC int INIT gunzip(unsigned char *buf, long len, 186 + long (*fill)(void*, unsigned long), 187 + long (*flush)(void*, unsigned long), 188 + unsigned char *out_buf, 189 + long *pos, 190 + void (*error)(char *x)) 191 + { 192 + return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error); 193 + } 194 + #else 195 + STATIC int INIT __decompress(unsigned char *buf, long len, 196 + long (*fill)(void*, unsigned long), 197 + long (*flush)(void*, unsigned long), 198 + unsigned char *out_buf, long out_len, 199 + long *pos, 200 + void (*error)(char *x)) 201 + { 202 + return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error); 203 + } 204 + #endif
+3 -3
lib/decompress_unlz4.c
··· 196 196 } 197 197 198 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 200 long (*fill)(void*, unsigned long), 201 201 long (*flush)(void*, unsigned long), 202 - unsigned char *output, 202 + unsigned char *output, long out_len, 203 203 long *posp, 204 - void(*error)(char *x) 204 + void (*error)(char *x) 205 205 ) 206 206 { 207 207 return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
+3 -4
lib/decompress_unlzma.c
··· 667 667 } 668 668 669 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 671 long (*fill)(void*, unsigned long), 672 672 long (*flush)(void*, unsigned long), 673 - unsigned char *output, 673 + unsigned char *output, long out_len, 674 674 long *posp, 675 - void(*error)(char *x) 676 - ) 675 + void (*error)(char *x)) 677 676 { 678 677 return unlzma(buf, in_len - 4, fill, flush, output, posp, error); 679 678 }
+12 -1
lib/decompress_unlzo.c
··· 31 31 */ 32 32 33 33 #ifdef STATIC 34 + #define PREBOOT 34 35 #include "lzo/lzo1x_decompress_safe.c" 35 36 #else 36 37 #include <linux/decompress/unlzo.h> ··· 288 287 return ret; 289 288 } 290 289 291 - #define decompress unlzo 290 + #ifdef PREBOOT 291 + STATIC int INIT __decompress(unsigned char *buf, long len, 292 + long (*fill)(void*, unsigned long), 293 + long (*flush)(void*, unsigned long), 294 + unsigned char *out_buf, long olen, 295 + long *pos, 296 + void (*error)(char *x)) 297 + { 298 + return unlzo(buf, len, fill, flush, out_buf, pos, error); 299 + } 300 + #endif
+11 -1
lib/decompress_unxz.c
··· 394 394 * This macro is used by architecture-specific files to decompress 395 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