diff options
Diffstat (limited to 'lib/lzo/lzo1x_decompress_safe.c')
| -rw-r--r-- | lib/lzo/lzo1x_decompress_safe.c | 257 | 
1 files changed, 257 insertions, 0 deletions
diff --git a/lib/lzo/lzo1x_decompress_safe.c b/lib/lzo/lzo1x_decompress_safe.c new file mode 100644 index 00000000000..8563081e8da --- /dev/null +++ b/lib/lzo/lzo1x_decompress_safe.c @@ -0,0 +1,257 @@ +/* + *  LZO1X Decompressor from LZO + * + *  Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com> + * + *  The full LZO package can be found at: + *  http://www.oberhumer.com/opensource/lzo/ + * + *  Changed for Linux kernel use by: + *  Nitin Gupta <nitingupta910@gmail.com> + *  Richard Purdie <rpurdie@openedhand.com> + */ + +#ifndef STATIC +#include <linux/module.h> +#include <linux/kernel.h> +#endif +#include <asm/unaligned.h> +#include <linux/lzo.h> +#include "lzodefs.h" + +#define HAVE_IP(t, x)					\ +	(((size_t)(ip_end - ip) >= (size_t)(t + x)) &&	\ +	 (((t + x) >= t) && ((t + x) >= x))) + +#define HAVE_OP(t, x)					\ +	(((size_t)(op_end - op) >= (size_t)(t + x)) &&	\ +	 (((t + x) >= t) && ((t + x) >= x))) + +#define NEED_IP(t, x)					\ +	do {						\ +		if (!HAVE_IP(t, x))			\ +			goto input_overrun;		\ +	} while (0) + +#define NEED_OP(t, x)					\ +	do {						\ +		if (!HAVE_OP(t, x))			\ +			goto output_overrun;		\ +	} while (0) + +#define TEST_LB(m_pos)					\ +	do {						\ +		if ((m_pos) < out)			\ +			goto lookbehind_overrun;	\ +	} while (0) + +int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, +			  unsigned char *out, size_t *out_len) +{ +	unsigned char *op; +	const unsigned char *ip; +	size_t t, next; +	size_t state = 0; +	const unsigned char *m_pos; +	const unsigned char * const ip_end = in + in_len; +	unsigned char * const op_end = out + *out_len; + +	op = out; +	ip = in; + +	if (unlikely(in_len < 3)) +		goto input_overrun; +	if (*ip > 17) { +		t = *ip++ - 17; +		if (t < 4) { +			next = t; +			goto match_next; +		} +		goto copy_literal_run; +	} + +	for (;;) { +		t = *ip++; +		if (t < 16) { +			if (likely(state == 0)) { +				if (unlikely(t == 0)) { +					while (unlikely(*ip == 0)) { +						t += 255; +						ip++; +						NEED_IP(1, 0); +					} +					t += 15 + *ip++; +				} +				t += 3; +copy_literal_run: +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) +				if (likely(HAVE_IP(t, 15) && HAVE_OP(t, 15))) { +					const unsigned char *ie = ip + t; +					unsigned char *oe = op + t; +					do { +						COPY8(op, ip); +						op += 8; +						ip += 8; +						COPY8(op, ip); +						op += 8; +						ip += 8; +					} while (ip < ie); +					ip = ie; +					op = oe; +				} else +#endif +				{ +					NEED_OP(t, 0); +					NEED_IP(t, 3); +					do { +						*op++ = *ip++; +					} while (--t > 0); +				} +				state = 4; +				continue; +			} else if (state != 4) { +				next = t & 3; +				m_pos = op - 1; +				m_pos -= t >> 2; +				m_pos -= *ip++ << 2; +				TEST_LB(m_pos); +				NEED_OP(2, 0); +				op[0] = m_pos[0]; +				op[1] = m_pos[1]; +				op += 2; +				goto match_next; +			} else { +				next = t & 3; +				m_pos = op - (1 + M2_MAX_OFFSET); +				m_pos -= t >> 2; +				m_pos -= *ip++ << 2; +				t = 3; +			} +		} else if (t >= 64) { +			next = t & 3; +			m_pos = op - 1; +			m_pos -= (t >> 2) & 7; +			m_pos -= *ip++ << 3; +			t = (t >> 5) - 1 + (3 - 1); +		} else if (t >= 32) { +			t = (t & 31) + (3 - 1); +			if (unlikely(t == 2)) { +				while (unlikely(*ip == 0)) { +					t += 255; +					ip++; +					NEED_IP(1, 0); +				} +				t += 31 + *ip++; +				NEED_IP(2, 0); +			} +			m_pos = op - 1; +			next = get_unaligned_le16(ip); +			ip += 2; +			m_pos -= next >> 2; +			next &= 3; +		} else { +			m_pos = op; +			m_pos -= (t & 8) << 11; +			t = (t & 7) + (3 - 1); +			if (unlikely(t == 2)) { +				while (unlikely(*ip == 0)) { +					t += 255; +					ip++; +					NEED_IP(1, 0); +				} +				t += 7 + *ip++; +				NEED_IP(2, 0); +			} +			next = get_unaligned_le16(ip); +			ip += 2; +			m_pos -= next >> 2; +			next &= 3; +			if (m_pos == op) +				goto eof_found; +			m_pos -= 0x4000; +		} +		TEST_LB(m_pos); +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) +		if (op - m_pos >= 8) { +			unsigned char *oe = op + t; +			if (likely(HAVE_OP(t, 15))) { +				do { +					COPY8(op, m_pos); +					op += 8; +					m_pos += 8; +					COPY8(op, m_pos); +					op += 8; +					m_pos += 8; +				} while (op < oe); +				op = oe; +				if (HAVE_IP(6, 0)) { +					state = next; +					COPY4(op, ip); +					op += next; +					ip += next; +					continue; +				} +			} else { +				NEED_OP(t, 0); +				do { +					*op++ = *m_pos++; +				} while (op < oe); +			} +		} else +#endif +		{ +			unsigned char *oe = op + t; +			NEED_OP(t, 0); +			op[0] = m_pos[0]; +			op[1] = m_pos[1]; +			op += 2; +			m_pos += 2; +			do { +				*op++ = *m_pos++; +			} while (op < oe); +		} +match_next: +		state = next; +		t = next; +#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) +		if (likely(HAVE_IP(6, 0) && HAVE_OP(4, 0))) { +			COPY4(op, ip); +			op += t; +			ip += t; +		} else +#endif +		{ +			NEED_IP(t, 3); +			NEED_OP(t, 0); +			while (t > 0) { +				*op++ = *ip++; +				t--; +			} +		} +	} + +eof_found: +	*out_len = op - out; +	return (t != 3       ? LZO_E_ERROR : +		ip == ip_end ? LZO_E_OK : +		ip <  ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN); + +input_overrun: +	*out_len = op - out; +	return LZO_E_INPUT_OVERRUN; + +output_overrun: +	*out_len = op - out; +	return LZO_E_OUTPUT_OVERRUN; + +lookbehind_overrun: +	*out_len = op - out; +	return LZO_E_LOOKBEHIND_OVERRUN; +} +#ifndef STATIC +EXPORT_SYMBOL_GPL(lzo1x_decompress_safe); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("LZO1X Decompressor"); + +#endif  | 
