blob: 2780e118a402635b81ae1b2f22b41c1d3a7051ed [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
14#include <common.h>
15#include <linux/lzo.h>
16#include <asm/byteorder.h>
17#include <asm/unaligned.h>
18#include "lzodefs.h"
19
20#define HAVE_IP(x, ip_end, ip) ((size_t)(ip_end - ip) < (x))
21#define HAVE_OP(x, op_end, op) ((size_t)(op_end - op) < (x))
22#define HAVE_LB(m_pos, out, op) (m_pos < out || m_pos >= op)
23
24#define COPY4(dst, src) \
25 put_unaligned(get_unaligned((const u32 *)(src)), (u32 *)(dst))
26
27int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
28 unsigned char *out, size_t *out_len)
29{
30 const unsigned char * const ip_end = in + in_len;
31 unsigned char * const op_end = out + *out_len;
32 const unsigned char *ip = in, *m_pos;
33 unsigned char *op = out;
34 size_t t;
35
36 *out_len = 0;
37
38 if (*ip > 17) {
39 t = *ip++ - 17;
40 if (t < 4)
41 goto match_next;
42 if (HAVE_OP(t, op_end, op))
43 goto output_overrun;
44 if (HAVE_IP(t + 1, ip_end, ip))
45 goto input_overrun;
46 do {
47 *op++ = *ip++;
48 } while (--t > 0);
49 goto first_literal_run;
50 }
51
52 while ((ip < ip_end)) {
53 t = *ip++;
54 if (t >= 16)
55 goto match;
56 if (t == 0) {
57 if (HAVE_IP(1, ip_end, ip))
58 goto input_overrun;
59 while (*ip == 0) {
60 t += 255;
61 ip++;
62 if (HAVE_IP(1, ip_end, ip))
63 goto input_overrun;
64 }
65 t += 15 + *ip++;
66 }
67 if (HAVE_OP(t + 3, op_end, op))
68 goto output_overrun;
69 if (HAVE_IP(t + 4, ip_end, ip))
70 goto input_overrun;
71
72 COPY4(op, ip);
73 op += 4;
74 ip += 4;
75 if (--t > 0) {
76 if (t >= 4) {
77 do {
78 COPY4(op, ip);
79 op += 4;
80 ip += 4;
81 t -= 4;
82 } while (t >= 4);
83 if (t > 0) {
84 do {
85 *op++ = *ip++;
86 } while (--t > 0);
87 }
88 } else {
89 do {
90 *op++ = *ip++;
91 } while (--t > 0);
92 }
93 }
94
95first_literal_run:
96 t = *ip++;
97 if (t >= 16)
98 goto match;
99 m_pos = op - (1 + M2_MAX_OFFSET);
100 m_pos -= t >> 2;
101 m_pos -= *ip++ << 2;
102
103 if (HAVE_LB(m_pos, out, op))
104 goto lookbehind_overrun;
105
106 if (HAVE_OP(3, op_end, op))
107 goto output_overrun;
108 *op++ = *m_pos++;
109 *op++ = *m_pos++;
110 *op++ = *m_pos;
111
112 goto match_done;
113
114 do {
115match:
116 if (t >= 64) {
117 m_pos = op - 1;
118 m_pos -= (t >> 2) & 7;
119 m_pos -= *ip++ << 3;
120 t = (t >> 5) - 1;
121 if (HAVE_LB(m_pos, out, op))
122 goto lookbehind_overrun;
123 if (HAVE_OP(t + 3 - 1, op_end, op))
124 goto output_overrun;
125 goto copy_match;
126 } else if (t >= 32) {
127 t &= 31;
128 if (t == 0) {
129 if (HAVE_IP(1, ip_end, ip))
130 goto input_overrun;
131 while (*ip == 0) {
132 t += 255;
133 ip++;
134 if (HAVE_IP(1, ip_end, ip))
135 goto input_overrun;
136 }
137 t += 31 + *ip++;
138 }
139 m_pos = op - 1;
140 m_pos -= get_unaligned_le16(ip) >> 2;
141 ip += 2;
142 } else if (t >= 16) {
143 m_pos = op;
144 m_pos -= (t & 8) << 11;
145
146 t &= 7;
147 if (t == 0) {
148 if (HAVE_IP(1, ip_end, ip))
149 goto input_overrun;
150 while (*ip == 0) {
151 t += 255;
152 ip++;
153 if (HAVE_IP(1, ip_end, ip))
154 goto input_overrun;
155 }
156 t += 7 + *ip++;
157 }
158 m_pos -= get_unaligned_le16(ip) >> 2;
159 ip += 2;
160 if (m_pos == op)
161 goto eof_found;
162 m_pos -= 0x4000;
163 } else {
164 m_pos = op - 1;
165 m_pos -= t >> 2;
166 m_pos -= *ip++ << 2;
167
168 if (HAVE_LB(m_pos, out, op))
169 goto lookbehind_overrun;
170 if (HAVE_OP(2, op_end, op))
171 goto output_overrun;
172
173 *op++ = *m_pos++;
174 *op++ = *m_pos;
175 goto match_done;
176 }
177
178 if (HAVE_LB(m_pos, out, op))
179 goto lookbehind_overrun;
180 if (HAVE_OP(t + 3 - 1, op_end, op))
181 goto output_overrun;
182
183 if (t >= 2 * 4 - (3 - 1) && (op - m_pos) >= 4) {
184 COPY4(op, m_pos);
185 op += 4;
186 m_pos += 4;
187 t -= 4 - (3 - 1);
188 do {
189 COPY4(op, m_pos);
190 op += 4;
191 m_pos += 4;
192 t -= 4;
193 } while (t >= 4);
194 if (t > 0)
195 do {
196 *op++ = *m_pos++;
197 } while (--t > 0);
198 } else {
199copy_match:
200 *op++ = *m_pos++;
201 *op++ = *m_pos++;
202 do {
203 *op++ = *m_pos++;
204 } while (--t > 0);
205 }
206match_done:
207 t = ip[-2] & 3;
208 if (t == 0)
209 break;
210match_next:
211 if (HAVE_OP(t, op_end, op))
212 goto output_overrun;
213 if (HAVE_IP(t + 1, ip_end, ip))
214 goto input_overrun;
215
216 *op++ = *ip++;
217 if (t > 1) {
218 *op++ = *ip++;
219 if (t > 2)
220 *op++ = *ip++;
221 }
222
223 t = *ip++;
224 } while (ip < ip_end);
225 }
226
227 *out_len = op - out;
228 return LZO_E_EOF_NOT_FOUND;
229
230eof_found:
231 *out_len = op - out;
232 return (ip == ip_end ? LZO_E_OK :
233 (ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
234input_overrun:
235 *out_len = op - out;
236 return LZO_E_INPUT_OVERRUN;
237
238output_overrun:
239 *out_len = op - out;
240 return LZO_E_OUTPUT_OVERRUN;
241
242lookbehind_overrun:
243 *out_len = op - out;
244 return LZO_E_LOOKBEHIND_OVERRUN;
245}