summaryrefslogtreecommitdiffstats
path: root/lib/decompress_inflate.c
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2015-09-09 18:39:12 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-09-10 16:29:01 -0400
commit2d3862d26e67a59340ba1cf1748196c76c5787de (patch)
treee3b1dd9157a0c745bcd8147cad82bf021a1cec72 /lib/decompress_inflate.c
parente852d82a5b55b44ce8be89078d0dfbddbeae3211 (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>
Diffstat (limited to 'lib/decompress_inflate.c')
-rw-r--r--lib/decompress_inflate.c31
1 files changed, 26 insertions, 5 deletions
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 */
36STATIC int INIT gunzip(unsigned char *buf, long len, 37STATIC 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
186STATIC 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
196STATIC 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