/src/barebox/lib/lzo/lzo1x_decompress_safe.c
Line | Count | Source |
1 | | /* |
2 | | * LZO1X Decompressor from LZO |
3 | | * |
4 | | * Copyright (C) 1996-2012 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 Linux kernel use by: |
10 | | * Nitin Gupta <nitingupta910@gmail.com> |
11 | | * Richard Purdie <rpurdie@openedhand.com> |
12 | | */ |
13 | | |
14 | | #include <asm/unaligned.h> |
15 | | #include <common.h> |
16 | | #include <lzo.h> |
17 | | #include "lzodefs.h" |
18 | | |
19 | 60.8k | #define HAVE_IP(x) ((size_t)(ip_end - ip) >= (size_t)(x)) |
20 | 35.4k | #define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x)) |
21 | 60.8k | #define NEED_IP(x) if (!HAVE_IP(x)) goto input_overrun |
22 | 35.4k | #define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun |
23 | 17.1k | #define TEST_LB(m_pos) if ((m_pos) < out) goto lookbehind_overrun |
24 | | |
25 | | /* This MAX_255_COUNT is the maximum number of times we can add 255 to a base |
26 | | * count without overflowing an integer. The multiply will overflow when |
27 | | * multiplying 255 by more than MAXINT/255. The sum will overflow earlier |
28 | | * depending on the base count. Since the base count is taken from a u8 |
29 | | * and a few bits, it is safe to assume that it will always be lower than |
30 | | * or equal to 2*255, thus we can always prevent any overflow by accepting |
31 | | * two less 255 steps. See Documentation/lzo.txt for more information. |
32 | | */ |
33 | | #define MAX_255_COUNT ((((size_t)~0) / 255) - 2) |
34 | | |
35 | | int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, |
36 | | unsigned char *out, size_t *out_len) |
37 | 303 | { |
38 | 303 | unsigned char *op; |
39 | 303 | const unsigned char *ip; |
40 | 303 | size_t t, next; |
41 | 303 | size_t state = 0; |
42 | 303 | const unsigned char *m_pos; |
43 | 303 | const unsigned char * const ip_end = in + in_len; |
44 | 303 | unsigned char * const op_end = out + *out_len; |
45 | | |
46 | 303 | op = out; |
47 | 303 | ip = in; |
48 | | |
49 | 303 | if (unlikely(in_len < 3)) |
50 | 19 | goto input_overrun; |
51 | 284 | if (*ip > 17) { |
52 | 128 | t = *ip++ - 17; |
53 | 128 | if (t < 4) { |
54 | 98 | next = t; |
55 | 98 | goto match_next; |
56 | 98 | } |
57 | 30 | goto copy_literal_run; |
58 | 128 | } |
59 | | |
60 | 18.4k | for (;;) { |
61 | 18.4k | t = *ip++; |
62 | 18.4k | if (t < 16) { |
63 | 3.13k | if (likely(state == 0)) { |
64 | 1.30k | if (unlikely(t == 0)) { |
65 | 852 | size_t offset; |
66 | 852 | const unsigned char *ip_last = ip; |
67 | | |
68 | 31.9k | while (unlikely(*ip == 0)) { |
69 | 31.0k | ip++; |
70 | 31.0k | NEED_IP(1); |
71 | 31.0k | } |
72 | 848 | offset = ip - ip_last; |
73 | 848 | if (unlikely(offset > MAX_255_COUNT)) |
74 | 0 | return LZO_E_ERROR; |
75 | | |
76 | 848 | offset = (offset << 8) - offset; |
77 | 848 | t += offset + 15 + *ip++; |
78 | 848 | } |
79 | 1.30k | t += 3; |
80 | 1.33k | copy_literal_run: |
81 | | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) |
82 | | if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) { |
83 | | const unsigned char *ie = ip + t; |
84 | | unsigned char *oe = op + t; |
85 | | do { |
86 | | COPY8(op, ip); |
87 | | op += 8; |
88 | | ip += 8; |
89 | | COPY8(op, ip); |
90 | | op += 8; |
91 | | ip += 8; |
92 | | } while (ip < ie); |
93 | | ip = ie; |
94 | | op = oe; |
95 | | } else |
96 | | #endif |
97 | 1.33k | { |
98 | 1.33k | NEED_OP(t); |
99 | 1.31k | NEED_IP(t + 3); |
100 | 321k | do { |
101 | 321k | *op++ = *ip++; |
102 | 321k | } while (--t > 0); |
103 | 1.26k | } |
104 | 0 | state = 4; |
105 | 1.26k | continue; |
106 | 1.82k | } else if (state != 4) { |
107 | 1.01k | next = t & 3; |
108 | 1.01k | m_pos = op - 1; |
109 | 1.01k | m_pos -= t >> 2; |
110 | 1.01k | m_pos -= *ip++ << 2; |
111 | 1.01k | TEST_LB(m_pos); |
112 | 1.00k | NEED_OP(2); |
113 | 1.00k | op[0] = m_pos[0]; |
114 | 1.00k | op[1] = m_pos[1]; |
115 | 1.00k | op += 2; |
116 | 1.00k | goto match_next; |
117 | 1.00k | } else { |
118 | 815 | next = t & 3; |
119 | 815 | m_pos = op - (1 + M2_MAX_OFFSET); |
120 | 815 | m_pos -= t >> 2; |
121 | 815 | m_pos -= *ip++ << 2; |
122 | 815 | t = 3; |
123 | 815 | } |
124 | 15.3k | } else if (t >= 64) { |
125 | 8.82k | next = t & 3; |
126 | 8.82k | m_pos = op - 1; |
127 | 8.82k | m_pos -= (t >> 2) & 7; |
128 | 8.82k | m_pos -= *ip++ << 3; |
129 | 8.82k | t = (t >> 5) - 1 + (3 - 1); |
130 | 8.82k | } else if (t >= 32) { |
131 | 1.16k | t = (t & 31) + (3 - 1); |
132 | 1.16k | if (unlikely(t == 2)) { |
133 | 318 | size_t offset; |
134 | 318 | const unsigned char *ip_last = ip; |
135 | | |
136 | 9.85k | while (unlikely(*ip == 0)) { |
137 | 9.53k | ip++; |
138 | 9.53k | NEED_IP(1); |
139 | 9.53k | } |
140 | 316 | offset = ip - ip_last; |
141 | 316 | if (unlikely(offset > MAX_255_COUNT)) |
142 | 0 | return LZO_E_ERROR; |
143 | | |
144 | 316 | offset = (offset << 8) - offset; |
145 | 316 | t += offset + 31 + *ip++; |
146 | 316 | NEED_IP(2); |
147 | 316 | } |
148 | 1.15k | m_pos = op - 1; |
149 | 1.15k | next = get_unaligned_le16(ip); |
150 | 1.15k | ip += 2; |
151 | 1.15k | m_pos -= next >> 2; |
152 | 1.15k | next &= 3; |
153 | 5.34k | } else { |
154 | 5.34k | m_pos = op; |
155 | 5.34k | m_pos -= (t & 8) << 11; |
156 | 5.34k | t = (t & 7) + (3 - 1); |
157 | 5.34k | if (unlikely(t == 2)) { |
158 | 487 | size_t offset; |
159 | 487 | const unsigned char *ip_last = ip; |
160 | | |
161 | 1.45k | while (unlikely(*ip == 0)) { |
162 | 976 | ip++; |
163 | 976 | NEED_IP(1); |
164 | 976 | } |
165 | 483 | offset = ip - ip_last; |
166 | 483 | if (unlikely(offset > MAX_255_COUNT)) |
167 | 0 | return LZO_E_ERROR; |
168 | | |
169 | 483 | offset = (offset << 8) - offset; |
170 | 483 | t += offset + 7 + *ip++; |
171 | 483 | NEED_IP(2); |
172 | 483 | } |
173 | 5.33k | next = get_unaligned_le16(ip); |
174 | 5.33k | ip += 2; |
175 | 5.33k | m_pos -= next >> 2; |
176 | 5.33k | next &= 3; |
177 | 5.33k | if (m_pos == op) |
178 | 33 | goto eof_found; |
179 | 5.30k | m_pos -= 0x4000; |
180 | 5.30k | } |
181 | 16.1k | TEST_LB(m_pos); |
182 | | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) |
183 | | if (op - m_pos >= 8) { |
184 | | unsigned char *oe = op + t; |
185 | | if (likely(HAVE_OP(t + 15))) { |
186 | | do { |
187 | | COPY8(op, m_pos); |
188 | | op += 8; |
189 | | m_pos += 8; |
190 | | COPY8(op, m_pos); |
191 | | op += 8; |
192 | | m_pos += 8; |
193 | | } while (op < oe); |
194 | | op = oe; |
195 | | if (HAVE_IP(6)) { |
196 | | state = next; |
197 | | COPY4(op, ip); |
198 | | op += next; |
199 | | ip += next; |
200 | | continue; |
201 | | } |
202 | | } else { |
203 | | NEED_OP(t); |
204 | | do { |
205 | | *op++ = *m_pos++; |
206 | | } while (op < oe); |
207 | | } |
208 | | } else |
209 | | #endif |
210 | 16.0k | { |
211 | 16.0k | unsigned char *oe = op + t; |
212 | 16.0k | NEED_OP(t); |
213 | 16.0k | op[0] = m_pos[0]; |
214 | 16.0k | op[1] = m_pos[1]; |
215 | 16.0k | op += 2; |
216 | 16.0k | m_pos += 2; |
217 | 772k | do { |
218 | 772k | *op++ = *m_pos++; |
219 | 772k | } while (op < oe); |
220 | 16.0k | } |
221 | 17.1k | match_next: |
222 | 17.1k | state = next; |
223 | 17.1k | t = next; |
224 | | #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) |
225 | | if (likely(HAVE_IP(6) && HAVE_OP(4))) { |
226 | | COPY4(op, ip); |
227 | | op += t; |
228 | | ip += t; |
229 | | } else |
230 | | #endif |
231 | 17.1k | { |
232 | 17.1k | NEED_IP(t + 3); |
233 | 17.0k | NEED_OP(t); |
234 | 33.8k | while (t > 0) { |
235 | 16.7k | *op++ = *ip++; |
236 | 16.7k | t--; |
237 | 16.7k | } |
238 | 17.0k | } |
239 | 17.0k | } |
240 | | |
241 | 33 | eof_found: |
242 | 33 | *out_len = op - out; |
243 | 33 | return (t != 3 ? LZO_E_ERROR : |
244 | 33 | ip == ip_end ? LZO_E_OK : |
245 | 24 | ip < ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN); |
246 | | |
247 | 153 | input_overrun: |
248 | 153 | *out_len = op - out; |
249 | 153 | return LZO_E_INPUT_OVERRUN; |
250 | | |
251 | 24 | output_overrun: |
252 | 24 | *out_len = op - out; |
253 | 24 | return LZO_E_OUTPUT_OVERRUN; |
254 | | |
255 | 93 | lookbehind_overrun: |
256 | 93 | *out_len = op - out; |
257 | 93 | return LZO_E_LOOKBEHIND_OVERRUN; |
258 | 156 | } |
259 | | |
260 | | EXPORT_SYMBOL(lzo1x_decompress_safe); |