aboutsummaryrefslogtreecommitdiffstats
path: root/lib/decompress_unlzo.c
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2011-01-12 20:01:21 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-13 11:03:24 -0500
commit5a3f81a7029daff5f08aad146f4c4510e790da49 (patch)
tree08f0dfe372ebb5db518de2e80c4fb1d7c81f70be /lib/decompress_unlzo.c
parent8f9b54a35a70b604ebd2b2f2e7e04eabd0ff8a54 (diff)
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: 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>
Diffstat (limited to 'lib/decompress_unlzo.c')
-rw-r--r--lib/decompress_unlzo.c46
1 files changed, 41 insertions, 5 deletions
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c
index 855d9d30ec45..7eb3b80bf022 100644
--- a/lib/decompress_unlzo.c
+++ b/lib/decompress_unlzo.c
@@ -48,14 +48,25 @@ static const unsigned char lzop_magic[] = {
48 48
49#define LZO_BLOCK_SIZE (256*1024l) 49#define LZO_BLOCK_SIZE (256*1024l)
50#define HEADER_HAS_FILTER 0x00000800L 50#define HEADER_HAS_FILTER 0x00000800L
51#define HEADER_SIZE_MIN (9 + 7 + 4 + 8 + 1 + 4)
52#define HEADER_SIZE_MAX (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
51 53
52STATIC inline int INIT parse_header(u8 *input, u8 *skip) 54STATIC inline int INIT parse_header(u8 *input, int *skip, int in_len)
53{ 55{
54 int l; 56 int l;
55 u8 *parse = input; 57 u8 *parse = input;
58 u8 *end = input + in_len;
56 u8 level = 0; 59 u8 level = 0;
57 u16 version; 60 u16 version;
58 61
62 /*
63 * Check that there's enough input to possibly have a valid header.
64 * Then it is possible to parse several fields until the minimum
65 * size may have been used.
66 */
67 if (in_len < HEADER_SIZE_MIN)
68 return 0;
69
59 /* read magic: 9 first bits */ 70 /* read magic: 9 first bits */
60 for (l = 0; l < 9; l++) { 71 for (l = 0; l < 9; l++) {
61 if (*parse++ != lzop_magic[l]) 72 if (*parse++ != lzop_magic[l])
@@ -73,6 +84,15 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
73 else 84 else
74 parse += 4; /* flags */ 85 parse += 4; /* flags */
75 86
87 /*
88 * At least mode, mtime_low, filename length, and checksum must
89 * be left to be parsed. If also mtime_high is present, it's OK
90 * because the next input buffer check is after reading the
91 * filename length.
92 */
93 if (end - parse < 8 + 1 + 4)
94 return 0;
95
76 /* skip mode and mtime_low */ 96 /* skip mode and mtime_low */
77 parse += 8; 97 parse += 8;
78 if (version >= 0x0940) 98 if (version >= 0x0940)
@@ -80,6 +100,8 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
80 100
81 l = *parse++; 101 l = *parse++;
82 /* don't care about the file name, and skip checksum */ 102 /* don't care about the file name, and skip checksum */
103 if (end - parse < l + 4)
104 return 0;
83 parse += l + 4; 105 parse += l + 4;
84 106
85 *skip = parse - input; 107 *skip = parse - input;
@@ -92,7 +114,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
92 u8 *output, int *posp, 114 u8 *output, int *posp,
93 void (*error) (char *x)) 115 void (*error) (char *x))
94{ 116{
95 u8 skip = 0, r = 0; 117 u8 r = 0;
118 int skip = 0;
96 u32 src_len, dst_len; 119 u32 src_len, dst_len;
97 size_t tmp; 120 size_t tmp;
98 u8 *in_buf, *in_buf_save, *out_buf; 121 u8 *in_buf, *in_buf_save, *out_buf;
@@ -134,19 +157,25 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
134 if (fill) 157 if (fill)
135 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); 158 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
136 159
137 if (!parse_header(input, &skip)) { 160 if (!parse_header(input, &skip, in_len)) {
138 error("invalid header"); 161 error("invalid header");
139 goto exit_2; 162 goto exit_2;
140 } 163 }
141 in_buf += skip; 164 in_buf += skip;
165 in_len -= skip;
142 166
143 if (posp) 167 if (posp)
144 *posp = skip; 168 *posp = skip;
145 169
146 for (;;) { 170 for (;;) {
147 /* read uncompressed block size */ 171 /* read uncompressed block size */
172 if (in_len < 4) {
173 error("file corrupted");
174 goto exit_2;
175 }
148 dst_len = get_unaligned_be32(in_buf); 176 dst_len = get_unaligned_be32(in_buf);
149 in_buf += 4; 177 in_buf += 4;
178 in_len -= 4;
150 179
151 /* exit if last block */ 180 /* exit if last block */
152 if (dst_len == 0) { 181 if (dst_len == 0) {
@@ -161,10 +190,15 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
161 } 190 }
162 191
163 /* read compressed block size, and skip block checksum info */ 192 /* read compressed block size, and skip block checksum info */
193 if (in_len < 8) {
194 error("file corrupted");
195 goto exit_2;
196 }
164 src_len = get_unaligned_be32(in_buf); 197 src_len = get_unaligned_be32(in_buf);
165 in_buf += 8; 198 in_buf += 8;
199 in_len -= 8;
166 200
167 if (src_len <= 0 || src_len > dst_len) { 201 if (src_len <= 0 || src_len > dst_len || src_len > in_len) {
168 error("file corrupted"); 202 error("file corrupted");
169 goto exit_2; 203 goto exit_2;
170 } 204 }
@@ -196,8 +230,10 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
196 if (fill) { 230 if (fill) {
197 in_buf = in_buf_save; 231 in_buf = in_buf_save;
198 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); 232 fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
199 } else 233 } else {
200 in_buf += src_len; 234 in_buf += src_len;
235 in_len -= src_len;
236 }
201 } 237 }
202 238
203 ret = 0; 239 ret = 0;