diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/decompress_unlzo.c | 46 | 
1 files changed, 41 insertions, 5 deletions
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c index 855d9d30ec4..7eb3b80bf02 100644 --- a/lib/decompress_unlzo.c +++ b/lib/decompress_unlzo.c @@ -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]) @@ -73,6 +84,15 @@ 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) @@ -80,6 +100,8 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)  	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; @@ -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; @@ -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) { @@ -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;  		} @@ -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;  |