diff options
author | Lasse Collin <lasse.collin@tukaani.org> | 2011-01-12 17:01:21 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 08:03:24 -0800 |
commit | fb7fa589fd3ecc212fabd7867a4ecc3b175260c1 (patch) | |
tree | 785e193aaaf9537136140084f8cf2f5ea0d1fb9f | |
parent | 5a3f81a7029daff5f08aad146f4c4510e790da49 (diff) |
Decompressors: fix callback-to-callback mode in decompress_unlzo.c
Callback-to-callback decompression mode is used for initrd (not
initramfs). The LZO wrapper is broken for this use case for two reasons:
- The argument validation is needlessly too strict by
requiring that "posp" is non-NULL when "fill" is non-NULL.
- The buffer handling code didn't work at all for this
use case.
I tested with LZO-compressed kernel, initramfs, initrd, and corrupt
(truncated) initramfs and initrd images.
Signed-off-by: Lasse Collin <lasse.collin@tukaani.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Alain Knaff <alain@knaff.lu>
Cc: Albin Tonnerre <albin.tonnerre@free-electrons.com>
Cc: Phillip Lougher <phillip@lougher.demon.co.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | lib/decompress_unlzo.c | 60 |
1 files changed, 50 insertions, 10 deletions
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c index 7eb3b80bf02..5a7a2adf4c4 100644 --- a/lib/decompress_unlzo.c +++ b/lib/decompress_unlzo.c @@ -139,8 +139,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len, goto exit_1; } else if (input) { in_buf = input; - } else if (!fill || !posp) { - error("NULL input pointer and missing position pointer or fill function"); + } else if (!fill) { + error("NULL input pointer and missing fill function"); goto exit_1; } else { in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE)); @@ -154,21 +154,40 @@ STATIC inline int INIT unlzo(u8 *input, int in_len, if (posp) *posp = 0; - if (fill) - fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); + if (fill) { + /* + * Start from in_buf + HEADER_SIZE_MAX to make it possible + * to use memcpy() to copy the unused data to the beginning + * of the buffer. This way memmove() isn't needed which + * is missing from pre-boot environments of most archs. + */ + in_buf += HEADER_SIZE_MAX; + in_len = fill(in_buf, HEADER_SIZE_MAX); + } - if (!parse_header(input, &skip, in_len)) { + if (!parse_header(in_buf, &skip, in_len)) { error("invalid header"); goto exit_2; } in_buf += skip; in_len -= skip; + if (fill) { + /* Move the unused data to the beginning of the buffer. */ + memcpy(in_buf_save, in_buf, in_len); + in_buf = in_buf_save; + } + if (posp) *posp = skip; for (;;) { /* read uncompressed block size */ + if (fill && in_len < 4) { + skip = fill(in_buf + in_len, 4 - in_len); + if (skip > 0) + in_len += skip; + } if (in_len < 4) { error("file corrupted"); goto exit_2; @@ -190,6 +209,11 @@ STATIC inline int INIT unlzo(u8 *input, int in_len, } /* read compressed block size, and skip block checksum info */ + if (fill && in_len < 8) { + skip = fill(in_buf + in_len, 8 - in_len); + if (skip > 0) + in_len += skip; + } if (in_len < 8) { error("file corrupted"); goto exit_2; @@ -198,12 +222,21 @@ STATIC inline int INIT unlzo(u8 *input, int in_len, in_buf += 8; in_len -= 8; - if (src_len <= 0 || src_len > dst_len || src_len > in_len) { + if (src_len <= 0 || src_len > dst_len) { error("file corrupted"); goto exit_2; } /* decompress */ + if (fill && in_len < src_len) { + skip = fill(in_buf + in_len, src_len - in_len); + if (skip > 0) + in_len += skip; + } + if (in_len < src_len) { + error("file corrupted"); + goto exit_2; + } tmp = dst_len; /* When the input data is not compressed at all, @@ -227,12 +260,19 @@ STATIC inline int INIT unlzo(u8 *input, int in_len, out_buf += dst_len; if (posp) *posp += src_len + 12; + + in_buf += src_len; + in_len -= src_len; if (fill) { + /* + * If there happens to still be unused data left in + * in_buf, move it to the beginning of the buffer. + * Use a loop to avoid memmove() dependency. + */ + if (in_len > 0) + for (skip = 0; skip < in_len; ++skip) + in_buf_save[skip] = in_buf[skip]; in_buf = in_buf_save; - fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); - } else { - in_buf += src_len; - in_len -= src_len; } } |