linux/lib/lzo/lzo1x_decompress_safe.c
Greg Kroah-Hartman 206a81c184 lzo: properly check for overruns
The lzo decompressor can, if given some really crazy data, possibly
overrun some variable types.  Modify the checking logic to properly
detect overruns before they happen.

Reported-by: "Don A. Bailey" <donb@securitymouse.com>
Tested-by: "Don A. Bailey" <donb@securitymouse.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2014-06-23 14:12:01 -04:00

258 lines
4.9 KiB
C

/*
* 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