109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b6bec26cSMarkus F.X.J. Oberhumer /*
38b975bd3SMarkus F.X.J. Oberhumer  *  LZO1X Decompressor from LZO
4b6bec26cSMarkus F.X.J. Oberhumer  *
58b975bd3SMarkus F.X.J. Oberhumer  *  Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com>
6b6bec26cSMarkus F.X.J. Oberhumer  *
7b6bec26cSMarkus F.X.J. Oberhumer  *  The full LZO package can be found at:
8b6bec26cSMarkus F.X.J. Oberhumer  *  http://www.oberhumer.com/opensource/lzo/
9b6bec26cSMarkus F.X.J. Oberhumer  *
108b975bd3SMarkus F.X.J. Oberhumer  *  Changed for Linux kernel use by:
11b6bec26cSMarkus F.X.J. Oberhumer  *  Nitin Gupta <nitingupta910@gmail.com>
12b6bec26cSMarkus F.X.J. Oberhumer  *  Richard Purdie <rpurdie@openedhand.com>
13b6bec26cSMarkus F.X.J. Oberhumer  */
14b6bec26cSMarkus F.X.J. Oberhumer 
15b6bec26cSMarkus F.X.J. Oberhumer #ifndef STATIC
16b6bec26cSMarkus F.X.J. Oberhumer #include <linux/module.h>
17b6bec26cSMarkus F.X.J. Oberhumer #include <linux/kernel.h>
18b6bec26cSMarkus F.X.J. Oberhumer #endif
19b6bec26cSMarkus F.X.J. Oberhumer #include <asm/unaligned.h>
20b6bec26cSMarkus F.X.J. Oberhumer #include <linux/lzo.h>
21b6bec26cSMarkus F.X.J. Oberhumer #include "lzodefs.h"
22b6bec26cSMarkus F.X.J. Oberhumer 
23af958a38SWilly Tarreau #define HAVE_IP(x)      ((size_t)(ip_end - ip) >= (size_t)(x))
24af958a38SWilly Tarreau #define HAVE_OP(x)      ((size_t)(op_end - op) >= (size_t)(x))
25af958a38SWilly Tarreau #define NEED_IP(x)      if (!HAVE_IP(x)) goto input_overrun
26af958a38SWilly Tarreau #define NEED_OP(x)      if (!HAVE_OP(x)) goto output_overrun
27af958a38SWilly Tarreau #define TEST_LB(m_pos)  if ((m_pos) < out) goto lookbehind_overrun
28b6bec26cSMarkus F.X.J. Oberhumer 
2972cf9012SWilly Tarreau /* This MAX_255_COUNT is the maximum number of times we can add 255 to a base
3072cf9012SWilly Tarreau  * count without overflowing an integer. The multiply will overflow when
3172cf9012SWilly Tarreau  * multiplying 255 by more than MAXINT/255. The sum will overflow earlier
3272cf9012SWilly Tarreau  * depending on the base count. Since the base count is taken from a u8
3372cf9012SWilly Tarreau  * and a few bits, it is safe to assume that it will always be lower than
3472cf9012SWilly Tarreau  * or equal to 2*255, thus we can always prevent any overflow by accepting
358e2a46a4SMauro Carvalho Chehab  * two less 255 steps. See Documentation/staging/lzo.rst for more information.
3672cf9012SWilly Tarreau  */
3772cf9012SWilly Tarreau #define MAX_255_COUNT      ((((size_t)~0) / 255) - 2)
3872cf9012SWilly Tarreau 
lzo1x_decompress_safe(const unsigned char * in,size_t in_len,unsigned char * out,size_t * out_len)39b6bec26cSMarkus F.X.J. Oberhumer int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
40b6bec26cSMarkus F.X.J. Oberhumer 			  unsigned char *out, size_t *out_len)
41b6bec26cSMarkus F.X.J. Oberhumer {
428b975bd3SMarkus F.X.J. Oberhumer 	unsigned char *op;
438b975bd3SMarkus F.X.J. Oberhumer 	const unsigned char *ip;
448b975bd3SMarkus F.X.J. Oberhumer 	size_t t, next;
458b975bd3SMarkus F.X.J. Oberhumer 	size_t state = 0;
468b975bd3SMarkus F.X.J. Oberhumer 	const unsigned char *m_pos;
47b6bec26cSMarkus F.X.J. Oberhumer 	const unsigned char * const ip_end = in + in_len;
48b6bec26cSMarkus F.X.J. Oberhumer 	unsigned char * const op_end = out + *out_len;
49b6bec26cSMarkus F.X.J. Oberhumer 
505ee4014aSDave Rodgman 	unsigned char bitstream_version;
515ee4014aSDave Rodgman 
528b975bd3SMarkus F.X.J. Oberhumer 	op = out;
538b975bd3SMarkus F.X.J. Oberhumer 	ip = in;
54b6bec26cSMarkus F.X.J. Oberhumer 
558b975bd3SMarkus F.X.J. Oberhumer 	if (unlikely(in_len < 3))
568b975bd3SMarkus F.X.J. Oberhumer 		goto input_overrun;
575ee4014aSDave Rodgman 
58b11ed18eSDave Rodgman 	if (likely(in_len >= 5) && likely(*ip == 17)) {
595ee4014aSDave Rodgman 		bitstream_version = ip[1];
605ee4014aSDave Rodgman 		ip += 2;
615ee4014aSDave Rodgman 	} else {
625ee4014aSDave Rodgman 		bitstream_version = 0;
635ee4014aSDave Rodgman 	}
645ee4014aSDave Rodgman 
65b6bec26cSMarkus F.X.J. Oberhumer 	if (*ip > 17) {
66b6bec26cSMarkus F.X.J. Oberhumer 		t = *ip++ - 17;
678b975bd3SMarkus F.X.J. Oberhumer 		if (t < 4) {
688b975bd3SMarkus F.X.J. Oberhumer 			next = t;
69b6bec26cSMarkus F.X.J. Oberhumer 			goto match_next;
708b975bd3SMarkus F.X.J. Oberhumer 		}
718b975bd3SMarkus F.X.J. Oberhumer 		goto copy_literal_run;
72b6bec26cSMarkus F.X.J. Oberhumer 	}
73b6bec26cSMarkus F.X.J. Oberhumer 
748b975bd3SMarkus F.X.J. Oberhumer 	for (;;) {
75b6bec26cSMarkus F.X.J. Oberhumer 		t = *ip++;
768b975bd3SMarkus F.X.J. Oberhumer 		if (t < 16) {
778b975bd3SMarkus F.X.J. Oberhumer 			if (likely(state == 0)) {
788b975bd3SMarkus F.X.J. Oberhumer 				if (unlikely(t == 0)) {
7972cf9012SWilly Tarreau 					size_t offset;
8072cf9012SWilly Tarreau 					const unsigned char *ip_last = ip;
8172cf9012SWilly Tarreau 
828b975bd3SMarkus F.X.J. Oberhumer 					while (unlikely(*ip == 0)) {
83b6bec26cSMarkus F.X.J. Oberhumer 						ip++;
84af958a38SWilly Tarreau 						NEED_IP(1);
85b6bec26cSMarkus F.X.J. Oberhumer 					}
8672cf9012SWilly Tarreau 					offset = ip - ip_last;
8772cf9012SWilly Tarreau 					if (unlikely(offset > MAX_255_COUNT))
8872cf9012SWilly Tarreau 						return LZO_E_ERROR;
8972cf9012SWilly Tarreau 
9072cf9012SWilly Tarreau 					offset = (offset << 8) - offset;
9172cf9012SWilly Tarreau 					t += offset + 15 + *ip++;
92b6bec26cSMarkus F.X.J. Oberhumer 				}
938b975bd3SMarkus F.X.J. Oberhumer 				t += 3;
948b975bd3SMarkus F.X.J. Oberhumer copy_literal_run:
958b975bd3SMarkus F.X.J. Oberhumer #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
96af958a38SWilly Tarreau 				if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) {
978b975bd3SMarkus F.X.J. Oberhumer 					const unsigned char *ie = ip + t;
988b975bd3SMarkus F.X.J. Oberhumer 					unsigned char *oe = op + t;
99b6bec26cSMarkus F.X.J. Oberhumer 					do {
1008b975bd3SMarkus F.X.J. Oberhumer 						COPY8(op, ip);
1018b975bd3SMarkus F.X.J. Oberhumer 						op += 8;
1028b975bd3SMarkus F.X.J. Oberhumer 						ip += 8;
1038b975bd3SMarkus F.X.J. Oberhumer 						COPY8(op, ip);
1048b975bd3SMarkus F.X.J. Oberhumer 						op += 8;
1058b975bd3SMarkus F.X.J. Oberhumer 						ip += 8;
1068b975bd3SMarkus F.X.J. Oberhumer 					} while (ip < ie);
1078b975bd3SMarkus F.X.J. Oberhumer 					ip = ie;
1088b975bd3SMarkus F.X.J. Oberhumer 					op = oe;
1098b975bd3SMarkus F.X.J. Oberhumer 				} else
1108b975bd3SMarkus F.X.J. Oberhumer #endif
1118b975bd3SMarkus F.X.J. Oberhumer 				{
112af958a38SWilly Tarreau 					NEED_OP(t);
113af958a38SWilly Tarreau 					NEED_IP(t + 3);
114b6bec26cSMarkus F.X.J. Oberhumer 					do {
115b6bec26cSMarkus F.X.J. Oberhumer 						*op++ = *ip++;
116b6bec26cSMarkus F.X.J. Oberhumer 					} while (--t > 0);
117b6bec26cSMarkus F.X.J. Oberhumer 				}
1188b975bd3SMarkus F.X.J. Oberhumer 				state = 4;
1198b975bd3SMarkus F.X.J. Oberhumer 				continue;
1208b975bd3SMarkus F.X.J. Oberhumer 			} else if (state != 4) {
1218b975bd3SMarkus F.X.J. Oberhumer 				next = t & 3;
1228b975bd3SMarkus F.X.J. Oberhumer 				m_pos = op - 1;
1238b975bd3SMarkus F.X.J. Oberhumer 				m_pos -= t >> 2;
1248b975bd3SMarkus F.X.J. Oberhumer 				m_pos -= *ip++ << 2;
1258b975bd3SMarkus F.X.J. Oberhumer 				TEST_LB(m_pos);
126af958a38SWilly Tarreau 				NEED_OP(2);
1278b975bd3SMarkus F.X.J. Oberhumer 				op[0] = m_pos[0];
1288b975bd3SMarkus F.X.J. Oberhumer 				op[1] = m_pos[1];
1298b975bd3SMarkus F.X.J. Oberhumer 				op += 2;
1308b975bd3SMarkus F.X.J. Oberhumer 				goto match_next;
131b6bec26cSMarkus F.X.J. Oberhumer 			} else {
1328b975bd3SMarkus F.X.J. Oberhumer 				next = t & 3;
133b6bec26cSMarkus F.X.J. Oberhumer 				m_pos = op - (1 + M2_MAX_OFFSET);
134b6bec26cSMarkus F.X.J. Oberhumer 				m_pos -= t >> 2;
135b6bec26cSMarkus F.X.J. Oberhumer 				m_pos -= *ip++ << 2;
1368b975bd3SMarkus F.X.J. Oberhumer 				t = 3;
1378b975bd3SMarkus F.X.J. Oberhumer 			}
1388b975bd3SMarkus F.X.J. Oberhumer 		} else if (t >= 64) {
1398b975bd3SMarkus F.X.J. Oberhumer 			next = t & 3;
140b6bec26cSMarkus F.X.J. Oberhumer 			m_pos = op - 1;
141b6bec26cSMarkus F.X.J. Oberhumer 			m_pos -= (t >> 2) & 7;
142b6bec26cSMarkus F.X.J. Oberhumer 			m_pos -= *ip++ << 3;
1438b975bd3SMarkus F.X.J. Oberhumer 			t = (t >> 5) - 1 + (3 - 1);
144b6bec26cSMarkus F.X.J. Oberhumer 		} else if (t >= 32) {
1458b975bd3SMarkus F.X.J. Oberhumer 			t = (t & 31) + (3 - 1);
1468b975bd3SMarkus F.X.J. Oberhumer 			if (unlikely(t == 2)) {
14772cf9012SWilly Tarreau 				size_t offset;
14872cf9012SWilly Tarreau 				const unsigned char *ip_last = ip;
14972cf9012SWilly Tarreau 
1508b975bd3SMarkus F.X.J. Oberhumer 				while (unlikely(*ip == 0)) {
151b6bec26cSMarkus F.X.J. Oberhumer 					ip++;
152af958a38SWilly Tarreau 					NEED_IP(1);
153b6bec26cSMarkus F.X.J. Oberhumer 				}
15472cf9012SWilly Tarreau 				offset = ip - ip_last;
15572cf9012SWilly Tarreau 				if (unlikely(offset > MAX_255_COUNT))
15672cf9012SWilly Tarreau 					return LZO_E_ERROR;
15772cf9012SWilly Tarreau 
15872cf9012SWilly Tarreau 				offset = (offset << 8) - offset;
15972cf9012SWilly Tarreau 				t += offset + 31 + *ip++;
160af958a38SWilly Tarreau 				NEED_IP(2);
161b6bec26cSMarkus F.X.J. Oberhumer 			}
162b6bec26cSMarkus F.X.J. Oberhumer 			m_pos = op - 1;
1638b975bd3SMarkus F.X.J. Oberhumer 			next = get_unaligned_le16(ip);
164b6bec26cSMarkus F.X.J. Oberhumer 			ip += 2;
1658b975bd3SMarkus F.X.J. Oberhumer 			m_pos -= next >> 2;
1668b975bd3SMarkus F.X.J. Oberhumer 			next &= 3;
1678b975bd3SMarkus F.X.J. Oberhumer 		} else {
1685ee4014aSDave Rodgman 			NEED_IP(2);
1695ee4014aSDave Rodgman 			next = get_unaligned_le16(ip);
1705ee4014aSDave Rodgman 			if (((next & 0xfffc) == 0xfffc) &&
1715ee4014aSDave Rodgman 			    ((t & 0xf8) == 0x18) &&
1725ee4014aSDave Rodgman 			    likely(bitstream_version)) {
1735ee4014aSDave Rodgman 				NEED_IP(3);
1745ee4014aSDave Rodgman 				t &= 7;
1755ee4014aSDave Rodgman 				t |= ip[2] << 3;
1765ee4014aSDave Rodgman 				t += MIN_ZERO_RUN_LENGTH;
1775ee4014aSDave Rodgman 				NEED_OP(t);
1785ee4014aSDave Rodgman 				memset(op, 0, t);
1795ee4014aSDave Rodgman 				op += t;
1805ee4014aSDave Rodgman 				next &= 3;
1815ee4014aSDave Rodgman 				ip += 3;
1825ee4014aSDave Rodgman 				goto match_next;
1835ee4014aSDave Rodgman 			} else {
184b6bec26cSMarkus F.X.J. Oberhumer 				m_pos = op;
185b6bec26cSMarkus F.X.J. Oberhumer 				m_pos -= (t & 8) << 11;
1868b975bd3SMarkus F.X.J. Oberhumer 				t = (t & 7) + (3 - 1);
1878b975bd3SMarkus F.X.J. Oberhumer 				if (unlikely(t == 2)) {
18872cf9012SWilly Tarreau 					size_t offset;
18972cf9012SWilly Tarreau 					const unsigned char *ip_last = ip;
19072cf9012SWilly Tarreau 
1918b975bd3SMarkus F.X.J. Oberhumer 					while (unlikely(*ip == 0)) {
192b6bec26cSMarkus F.X.J. Oberhumer 						ip++;
193af958a38SWilly Tarreau 						NEED_IP(1);
194b6bec26cSMarkus F.X.J. Oberhumer 					}
19572cf9012SWilly Tarreau 					offset = ip - ip_last;
19672cf9012SWilly Tarreau 					if (unlikely(offset > MAX_255_COUNT))
19772cf9012SWilly Tarreau 						return LZO_E_ERROR;
19872cf9012SWilly Tarreau 
19972cf9012SWilly Tarreau 					offset = (offset << 8) - offset;
20072cf9012SWilly Tarreau 					t += offset + 7 + *ip++;
201af958a38SWilly Tarreau 					NEED_IP(2);
2028b975bd3SMarkus F.X.J. Oberhumer 					next = get_unaligned_le16(ip);
2035ee4014aSDave Rodgman 				}
204b6bec26cSMarkus F.X.J. Oberhumer 				ip += 2;
2058b975bd3SMarkus F.X.J. Oberhumer 				m_pos -= next >> 2;
2068b975bd3SMarkus F.X.J. Oberhumer 				next &= 3;
207b6bec26cSMarkus F.X.J. Oberhumer 				if (m_pos == op)
208b6bec26cSMarkus F.X.J. Oberhumer 					goto eof_found;
209b6bec26cSMarkus F.X.J. Oberhumer 				m_pos -= 0x4000;
210b6bec26cSMarkus F.X.J. Oberhumer 			}
2115ee4014aSDave Rodgman 		}
2128b975bd3SMarkus F.X.J. Oberhumer 		TEST_LB(m_pos);
2138b975bd3SMarkus F.X.J. Oberhumer #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
2148b975bd3SMarkus F.X.J. Oberhumer 		if (op - m_pos >= 8) {
2158b975bd3SMarkus F.X.J. Oberhumer 			unsigned char *oe = op + t;
216af958a38SWilly Tarreau 			if (likely(HAVE_OP(t + 15))) {
217b6bec26cSMarkus F.X.J. Oberhumer 				do {
2188b975bd3SMarkus F.X.J. Oberhumer 					COPY8(op, m_pos);
2198b975bd3SMarkus F.X.J. Oberhumer 					op += 8;
2208b975bd3SMarkus F.X.J. Oberhumer 					m_pos += 8;
2218b975bd3SMarkus F.X.J. Oberhumer 					COPY8(op, m_pos);
2228b975bd3SMarkus F.X.J. Oberhumer 					op += 8;
2238b975bd3SMarkus F.X.J. Oberhumer 					m_pos += 8;
2248b975bd3SMarkus F.X.J. Oberhumer 				} while (op < oe);
2258b975bd3SMarkus F.X.J. Oberhumer 				op = oe;
226af958a38SWilly Tarreau 				if (HAVE_IP(6)) {
2278b975bd3SMarkus F.X.J. Oberhumer 					state = next;
2288b975bd3SMarkus F.X.J. Oberhumer 					COPY4(op, ip);
2298b975bd3SMarkus F.X.J. Oberhumer 					op += next;
2308b975bd3SMarkus F.X.J. Oberhumer 					ip += next;
2318b975bd3SMarkus F.X.J. Oberhumer 					continue;
232b6bec26cSMarkus F.X.J. Oberhumer 				}
2338b975bd3SMarkus F.X.J. Oberhumer 			} else {
234af958a38SWilly Tarreau 				NEED_OP(t);
2358b975bd3SMarkus F.X.J. Oberhumer 				do {
2368b975bd3SMarkus F.X.J. Oberhumer 					*op++ = *m_pos++;
2378b975bd3SMarkus F.X.J. Oberhumer 				} while (op < oe);
2388b975bd3SMarkus F.X.J. Oberhumer 			}
2398b975bd3SMarkus F.X.J. Oberhumer 		} else
2408b975bd3SMarkus F.X.J. Oberhumer #endif
2418b975bd3SMarkus F.X.J. Oberhumer 		{
2428b975bd3SMarkus F.X.J. Oberhumer 			unsigned char *oe = op + t;
243af958a38SWilly Tarreau 			NEED_OP(t);
2448b975bd3SMarkus F.X.J. Oberhumer 			op[0] = m_pos[0];
2458b975bd3SMarkus F.X.J. Oberhumer 			op[1] = m_pos[1];
2468b975bd3SMarkus F.X.J. Oberhumer 			op += 2;
2478b975bd3SMarkus F.X.J. Oberhumer 			m_pos += 2;
2488b975bd3SMarkus F.X.J. Oberhumer 			do {
2498b975bd3SMarkus F.X.J. Oberhumer 				*op++ = *m_pos++;
2508b975bd3SMarkus F.X.J. Oberhumer 			} while (op < oe);
2518b975bd3SMarkus F.X.J. Oberhumer 		}
252b6bec26cSMarkus F.X.J. Oberhumer match_next:
2538b975bd3SMarkus F.X.J. Oberhumer 		state = next;
2548b975bd3SMarkus F.X.J. Oberhumer 		t = next;
2558b975bd3SMarkus F.X.J. Oberhumer #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
256af958a38SWilly Tarreau 		if (likely(HAVE_IP(6) && HAVE_OP(4))) {
2578b975bd3SMarkus F.X.J. Oberhumer 			COPY4(op, ip);
2588b975bd3SMarkus F.X.J. Oberhumer 			op += t;
2598b975bd3SMarkus F.X.J. Oberhumer 			ip += t;
2608b975bd3SMarkus F.X.J. Oberhumer 		} else
2618b975bd3SMarkus F.X.J. Oberhumer #endif
2628b975bd3SMarkus F.X.J. Oberhumer 		{
263af958a38SWilly Tarreau 			NEED_IP(t + 3);
264af958a38SWilly Tarreau 			NEED_OP(t);
2658b975bd3SMarkus F.X.J. Oberhumer 			while (t > 0) {
266b6bec26cSMarkus F.X.J. Oberhumer 				*op++ = *ip++;
2678b975bd3SMarkus F.X.J. Oberhumer 				t--;
268b6bec26cSMarkus F.X.J. Oberhumer 			}
269b6bec26cSMarkus F.X.J. Oberhumer 		}
2708b975bd3SMarkus F.X.J. Oberhumer 	}
271b6bec26cSMarkus F.X.J. Oberhumer 
272b6bec26cSMarkus F.X.J. Oberhumer eof_found:
273b6bec26cSMarkus F.X.J. Oberhumer 	*out_len = op - out;
2748b975bd3SMarkus F.X.J. Oberhumer 	return (t != 3       ? LZO_E_ERROR :
2758b975bd3SMarkus F.X.J. Oberhumer 		ip == ip_end ? LZO_E_OK :
2768b975bd3SMarkus F.X.J. Oberhumer 		ip <  ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN);
2778b975bd3SMarkus F.X.J. Oberhumer 
278b6bec26cSMarkus F.X.J. Oberhumer input_overrun:
279b6bec26cSMarkus F.X.J. Oberhumer 	*out_len = op - out;
280b6bec26cSMarkus F.X.J. Oberhumer 	return LZO_E_INPUT_OVERRUN;
281b6bec26cSMarkus F.X.J. Oberhumer 
282b6bec26cSMarkus F.X.J. Oberhumer output_overrun:
283b6bec26cSMarkus F.X.J. Oberhumer 	*out_len = op - out;
284b6bec26cSMarkus F.X.J. Oberhumer 	return LZO_E_OUTPUT_OVERRUN;
285b6bec26cSMarkus F.X.J. Oberhumer 
286b6bec26cSMarkus F.X.J. Oberhumer lookbehind_overrun:
287b6bec26cSMarkus F.X.J. Oberhumer 	*out_len = op - out;
288b6bec26cSMarkus F.X.J. Oberhumer 	return LZO_E_LOOKBEHIND_OVERRUN;
289b6bec26cSMarkus F.X.J. Oberhumer }
290b6bec26cSMarkus F.X.J. Oberhumer #ifndef STATIC
291b6bec26cSMarkus F.X.J. Oberhumer EXPORT_SYMBOL_GPL(lzo1x_decompress_safe);
292b6bec26cSMarkus F.X.J. Oberhumer 
293b6bec26cSMarkus F.X.J. Oberhumer MODULE_LICENSE("GPL");
294b6bec26cSMarkus F.X.J. Oberhumer MODULE_DESCRIPTION("LZO1X Decompressor");
295b6bec26cSMarkus F.X.J. Oberhumer 
296b6bec26cSMarkus F.X.J. Oberhumer #endif
297