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 /lib/decompress_inflate.c | |
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>
Diffstat (limited to 'lib/decompress_inflate.c')
-rw-r--r-- | lib/decompress_inflate.c | 31 |
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 */ |
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 | ||