Skip to content

Commit

Permalink
Decompressors: check input size in decompress_unlzo.c
Browse files Browse the repository at this point in the history
The code assumes that the input is valid and not truncated.  Add checks to
avoid reading past the end of the input buffer.  Change the type of "skip"
from u8 to int to fix a possible integer overflow.

Signed-off-by: Lasse Collin <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: Alain Knaff <[email protected]>
Cc: Albin Tonnerre <[email protected]>
Cc: Phillip Lougher <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Larhzu authored and torvalds committed Jan 13, 2011
1 parent 8f9b54a commit 5a3f81a
Showing 1 changed file with 41 additions and 5 deletions.
46 changes: 41 additions & 5 deletions lib/decompress_unlzo.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,25 @@ static const unsigned char lzop_magic[] = {

#define LZO_BLOCK_SIZE (256*1024l)
#define HEADER_HAS_FILTER 0x00000800L
#define HEADER_SIZE_MIN (9 + 7 + 4 + 8 + 1 + 4)
#define HEADER_SIZE_MAX (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)

STATIC inline int INIT parse_header(u8 *input, u8 *skip)
STATIC inline int INIT parse_header(u8 *input, int *skip, int in_len)
{
int l;
u8 *parse = input;
u8 *end = input + in_len;
u8 level = 0;
u16 version;

/*
* Check that there's enough input to possibly have a valid header.
* Then it is possible to parse several fields until the minimum
* size may have been used.
*/
if (in_len < HEADER_SIZE_MIN)
return 0;

/* read magic: 9 first bits */
for (l = 0; l < 9; l++) {
if (*parse++ != lzop_magic[l])
Expand All @@ -73,13 +84,24 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
else
parse += 4; /* flags */

/*
* At least mode, mtime_low, filename length, and checksum must
* be left to be parsed. If also mtime_high is present, it's OK
* because the next input buffer check is after reading the
* filename length.
*/
if (end - parse < 8 + 1 + 4)
return 0;

/* skip mode and mtime_low */
parse += 8;
if (version >= 0x0940)
parse += 4; /* skip mtime_high */

l = *parse++;
/* don't care about the file name, and skip checksum */
if (end - parse < l + 4)
return 0;
parse += l + 4;

*skip = parse - input;
Expand All @@ -92,7 +114,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
u8 *output, int *posp,
void (*error) (char *x))
{
u8 skip = 0, r = 0;
u8 r = 0;
int skip = 0;
u32 src_len, dst_len;
size_t tmp;
u8 *in_buf, *in_buf_save, *out_buf;
Expand Down Expand Up @@ -134,19 +157,25 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
if (fill)
fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));

if (!parse_header(input, &skip)) {
if (!parse_header(input, &skip, in_len)) {
error("invalid header");
goto exit_2;
}
in_buf += skip;
in_len -= skip;

if (posp)
*posp = skip;

for (;;) {
/* read uncompressed block size */
if (in_len < 4) {
error("file corrupted");
goto exit_2;
}
dst_len = get_unaligned_be32(in_buf);
in_buf += 4;
in_len -= 4;

/* exit if last block */
if (dst_len == 0) {
Expand All @@ -161,10 +190,15 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
}

/* read compressed block size, and skip block checksum info */
if (in_len < 8) {
error("file corrupted");
goto exit_2;
}
src_len = get_unaligned_be32(in_buf);
in_buf += 8;
in_len -= 8;

if (src_len <= 0 || src_len > dst_len) {
if (src_len <= 0 || src_len > dst_len || src_len > in_len) {
error("file corrupted");
goto exit_2;
}
Expand Down Expand Up @@ -196,8 +230,10 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
if (fill) {
in_buf = in_buf_save;
fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
} else
} else {
in_buf += src_len;
in_len -= src_len;
}
}

ret = 0;
Expand Down

0 comments on commit 5a3f81a

Please sign in to comment.