Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5a3f81a7 authored by Lasse Collin's avatar Lasse Collin Committed by Linus Torvalds
Browse files

Decompressors: check input size in decompress_unlzo.c



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: default avatarLasse 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: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 8f9b54a3
Loading
Loading
Loading
Loading
+41 −5
Original line number Original line Diff line number Diff line
@@ -48,14 +48,25 @@ static const unsigned char lzop_magic[] = {


#define LZO_BLOCK_SIZE        (256*1024l)
#define LZO_BLOCK_SIZE        (256*1024l)
#define HEADER_HAS_FILTER      0x00000800L
#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;
	int l;
	u8 *parse = input;
	u8 *parse = input;
	u8 *end = input + in_len;
	u8 level = 0;
	u8 level = 0;
	u16 version;
	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 */
	/* read magic: 9 first bits */
	for (l = 0; l < 9; l++) {
	for (l = 0; l < 9; l++) {
		if (*parse++ != lzop_magic[l])
		if (*parse++ != lzop_magic[l])
@@ -73,6 +84,15 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
	else
	else
		parse += 4; /* flags */
		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 */
	/* skip mode and mtime_low */
	parse += 8;
	parse += 8;
	if (version >= 0x0940)
	if (version >= 0x0940)
@@ -80,6 +100,8 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)


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


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


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


	if (posp)
	if (posp)
		*posp = skip;
		*posp = skip;


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


		/* exit if last block */
		/* exit if last block */
		if (dst_len == 0) {
		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 */
		/* 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);
		src_len = get_unaligned_be32(in_buf);
		in_buf += 8;
		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");
			error("file corrupted");
			goto exit_2;
			goto exit_2;
		}
		}
@@ -196,8 +230,10 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
		if (fill) {
		if (fill) {
			in_buf = in_buf_save;
			in_buf = in_buf_save;
			fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
			fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
		} else
		} else {
			in_buf += src_len;
			in_buf += src_len;
			in_len -= src_len;
		}
	}
	}


	ret = 0;
	ret = 0;