blob: 5d70fa4133796ae652273f04714e26a94ef42adb [file] [log] [blame]
Stefan Roese371abe82009-03-19 15:34:56 +01001/*
2 * LZO1X Decompressor from MiniLZO
3 *
4 * Copyright (C) 1996-2005 Markus F.X.J. Oberhumer <markus@oberhumer.com>
5 *
6 * The full LZO package can be found at:
7 * http://www.oberhumer.com/opensource/lzo/
8 *
9 * Changed for kernel use by:
10 * Nitin Gupta <nitingupta910@gmail.com>
11 * Richard Purdie <rpurdie@openedhand.com>
12 */
13
Tom Rinia877ce12023-12-14 13:16:58 -050014#include <linux/kernel.h>
Stefan Roese371abe82009-03-19 15:34:56 +010015#include <linux/lzo.h>
Tom Rinia877ce12023-12-14 13:16:58 -050016#include <linux/string.h>
Stefan Roese371abe82009-03-19 15:34:56 +010017#include <asm/byteorder.h>
18#include <asm/unaligned.h>
19#include "lzodefs.h"
20
21#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x))
22#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x))
23#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op)
24
25#define COPY4(dst, src) \
26 put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
27
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010028static const unsigned char lzop_magic[] = {
29 0x89, 0x4c, 0x5a, 0x4f, 0x00, 0x0d, 0x0a, 0x1a, 0x0a
30};
31
32#define HEADER_HAS_FILTER 0x00000800L
33
Jean-Jacques Hiblotbf44da22017-09-15 12:57:28 +020034
35bool lzop_is_valid_header(const unsigned char *src)
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010036{
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010037 int i;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010038 /* read magic: 9 first bytes */
39 for (i = 0; i < ARRAY_SIZE(lzop_magic); i++) {
40 if (*src++ != lzop_magic[i])
Jean-Jacques Hiblotbf44da22017-09-15 12:57:28 +020041 return false;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010042 }
Jean-Jacques Hiblotbf44da22017-09-15 12:57:28 +020043 return true;
44}
45
46static inline const unsigned char *parse_header(const unsigned char *src)
47{
48 u16 version;
49 int i;
50
51 if (!lzop_is_valid_header(src))
52 return NULL;
53
54 /* skip header */
55 src += 9;
56
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010057 /* get version (2bytes), skip library version (2),
58 * 'need to be extracted' version (2) and
59 * method (1) */
60 version = get_unaligned_be16(src);
61 src += 7;
62 if (version >= 0x0940)
Marek Vasutb7cb6f72011-09-30 12:13:26 +020063 src++;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010064 if (get_unaligned_be32(src) & HEADER_HAS_FILTER)
65 src += 4; /* filter info */
66
67 /* skip flags, mode and mtime_low */
68 src += 12;
69 if (version >= 0x0940)
70 src += 4; /* skip mtime_high */
71
72 i = *src++;
73 /* don't care about the file name, and skip checksum */
74 src += i + 4;
75
76 return src;
77}
78
79int lzop_decompress(const unsigned char *src, size_t src_len,
80 unsigned char *dst, size_t *dst_len)
81{
82 unsigned char *start = dst;
83 const unsigned char *send = src + src_len;
84 u32 slen, dlen;
Kees Cookd465f882013-08-16 07:59:15 -070085 size_t tmp, remaining;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010086 int r;
87
88 src = parse_header(src);
89 if (!src)
90 return LZO_E_ERROR;
91
Kees Cookd465f882013-08-16 07:59:15 -070092 remaining = *dst_len;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +010093 while (src < send) {
94 /* read uncompressed block size */
95 dlen = get_unaligned_be32(src);
96 src += 4;
97
98 /* exit if last block */
99 if (dlen == 0) {
100 *dst_len = dst - start;
101 return LZO_E_OK;
102 }
103
104 /* read compressed block size, and skip block checksum info */
105 slen = get_unaligned_be32(src);
106 src += 8;
107
108 if (slen <= 0 || slen > dlen)
109 return LZO_E_ERROR;
110
Kees Cookd465f882013-08-16 07:59:15 -0700111 /* abort if buffer ran out of room */
112 if (dlen > remaining)
113 return LZO_E_OUTPUT_OVERRUN;
114
Joris Lijssens86049ad2016-06-17 10:46:58 +0200115 /* When the input data is not compressed at all,
116 * lzo1x_decompress_safe will fail, so call memcpy()
117 * instead */
118 if (dlen == slen) {
119 memcpy(dst, src, slen);
120 } else {
121 /* decompress */
122 tmp = dlen;
123 r = lzo1x_decompress_safe((u8 *)src, slen, dst, &tmp);
Peter Korsgaardf7440cd2009-11-19 11:37:51 +0100124
Joris Lijssens86049ad2016-06-17 10:46:58 +0200125 if (r != LZO_E_OK) {
126 *dst_len = dst - start;
127 return r;
128 }
Peter Korsgaardf7440cd2009-11-19 11:37:51 +0100129
Joris Lijssens86049ad2016-06-17 10:46:58 +0200130 if (dlen != tmp)
131 return LZO_E_ERROR;
132 }
Peter Korsgaardf7440cd2009-11-19 11:37:51 +0100133
134 src += slen;
135 dst += dlen;
Kees Cookd465f882013-08-16 07:59:15 -0700136 remaining -= dlen;
Peter Korsgaardf7440cd2009-11-19 11:37:51 +0100137 }
138
139 return LZO_E_INPUT_OVERRUN;
140}
141
Stefan Roese371abe82009-03-19 15:34:56 +0100142int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
143 unsigned char *out, size_t *out_len)
144{
145 const unsigned char * const ip_end = in + in_len;
146 unsigned char * const op_end = out + *out_len;
147 const unsigned char *ip = in, *m_pos;
148 unsigned char *op = out;
149 size_t t;
150
151 *out_len = 0;
152
153 if (*ip > 17) {
154 t = *ip++ - 17;
155 if (t < 4)
156 goto match_next;
157 if (HAVE_OP(t, op_end, op))
158 goto output_overrun;
159 if (HAVE_IP(t + 1, ip_end, ip))
160 goto input_overrun;
161 do {
162 *op++ = *ip++;
163 } while (--t > 0);
164 goto first_literal_run;
165 }
166
167 while ((ip < ip_end)) {
168 t = *ip++;
169 if (t >= 16)
170 goto match;
171 if (t == 0) {
172 if (HAVE_IP(1, ip_end, ip))
173 goto input_overrun;
174 while (*ip == 0) {
175 t += 255;
176 ip++;
177 if (HAVE_IP(1, ip_end, ip))
178 goto input_overrun;
179 }
180 t += 15 + *ip++;
181 }
182 if (HAVE_OP(t + 3, op_end, op))
183 goto output_overrun;
184 if (HAVE_IP(t + 4, ip_end, ip))
185 goto input_overrun;
186
187 COPY4(op, ip);
188 op += 4;
189 ip += 4;
190 if (--t > 0) {
191 if (t >= 4) {
192 do {
193 COPY4(op, ip);
194 op += 4;
195 ip += 4;
196 t -= 4;
197 } while (t >= 4);
198 if (t > 0) {
199 do {
200 *op++ = *ip++;
201 } while (--t > 0);
202 }
203 } else {
204 do {
205 *op++ = *ip++;
206 } while (--t > 0);
207 }
208 }
209
210first_literal_run:
211 t = *ip++;
212 if (t >= 16)
213 goto match;
214 m_pos = op - (1 + M2_MAX_OFFSET);
215 m_pos -= t >> 2;
216 m_pos -= *ip++ << 2;
217
218 if (HAVE_LB(m_pos, out, op))
219 goto lookbehind_overrun;
220
221 if (HAVE_OP(3, op_end, op))
222 goto output_overrun;
223 *op++ = *m_pos++;
224 *op++ = *m_pos++;
225 *op++ = *m_pos;
226
227 goto match_done;
228
229 do {
230match:
231 if (t >= 64) {
232 m_pos = op - 1;
233 m_pos -= (t >> 2) & 7;
234 m_pos -= *ip++ << 3;
235 t = (t >> 5) - 1;
236 if (HAVE_LB(m_pos, out, op))
237 goto lookbehind_overrun;
238 if (HAVE_OP(t + 3 - 1, op_end, op))
239 goto output_overrun;
240 goto copy_match;
241 } else if (t >= 32) {
242 t &= 31;
243 if (t == 0) {
244 if (HAVE_IP(1, ip_end, ip))
245 goto input_overrun;
246 while (*ip == 0) {
247 t += 255;
248 ip++;
249 if (HAVE_IP(1, ip_end, ip))
250 goto input_overrun;
251 }
252 t += 31 + *ip++;
253 }
254 m_pos = op - 1;
255 m_pos -= get_unaligned_le16(ip) >> 2;
256 ip += 2;
257 } else if (t >= 16) {
258 m_pos = op;
259 m_pos -= (t & 8) << 11;
260
261 t &= 7;
262 if (t == 0) {
263 if (HAVE_IP(1, ip_end, ip))
264 goto input_overrun;
265 while (*ip == 0) {
266 t += 255;
267 ip++;
268 if (HAVE_IP(1, ip_end, ip))
269 goto input_overrun;
270 }
271 t += 7 + *ip++;
272 }
273 m_pos -= get_unaligned_le16(ip) >> 2;
274 ip += 2;
275 if (m_pos == op)
276 goto eof_found;
277 m_pos -= 0x4000;
278 } else {
279 m_pos = op - 1;
280 m_pos -= t >> 2;
281 m_pos -= *ip++ << 2;
282
283 if (HAVE_LB(m_pos, out, op))
284 goto lookbehind_overrun;
285 if (HAVE_OP(2, op_end, op))
286 goto output_overrun;
287
288 *op++ = *m_pos++;
289 *op++ = *m_pos;
290 goto match_done;
291 }
292
293 if (HAVE_LB(m_pos, out, op))
294 goto lookbehind_overrun;
295 if (HAVE_OP(t + 3 - 1, op_end, op))
296 goto output_overrun;
297
298 if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) {
299 COPY4(op, m_pos);
300 op += 4;
301 m_pos += 4;
302 t -= 4 - (3 - 1);
303 do {
304 COPY4(op, m_pos);
305 op += 4;
306 m_pos += 4;
307 t -= 4;
308 } while (t >= 4);
309 if (t > 0)
310 do {
311 *op++ = *m_pos++;
312 } while (--t > 0);
313 } else {
314copy_match:
315 *op++ = *m_pos++;
316 *op++ = *m_pos++;
317 do {
318 *op++ = *m_pos++;
319 } while (--t > 0);
320 }
321match_done:
322 t = ip[-2] & 3;
323 if (t == 0)
324 break;
325match_next:
326 if (HAVE_OP(t, op_end, op))
327 goto output_overrun;
328 if (HAVE_IP(t + 1, ip_end, ip))
329 goto input_overrun;
330
331 *op++ = *ip++;
332 if (t > 1) {
333 *op++ = *ip++;
334 if (t > 2)
335 *op++ = *ip++;
336 }
337
338 t = *ip++;
339 } while (ip < ip_end);
340 }
341
342 *out_len = op - out;
343 return LZO_E_EOF_NOT_FOUND;
344
345eof_found:
346 *out_len = op - out;
347 return (ip == ip_end ? LZO_E_OK :
348 (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
349input_overrun:
350 *out_len = op - out;
351 return LZO_E_INPUT_OVERRUN;
352
353output_overrun:
354 *out_len = op - out;
355 return LZO_E_OUTPUT_OVERRUN;
356
357lookbehind_overrun:
358 *out_len = op - out;
359 return LZO_E_LOOKBEHIND_OVERRUN;
360}