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>
This commit is contained in:
Greg Kroah-Hartman 2014-06-20 22:00:53 -07:00
parent 7171511eae
commit 206a81c184

View File

@ -19,11 +19,31 @@
#include <linux/lzo.h> #include <linux/lzo.h>
#include "lzodefs.h" #include "lzodefs.h"
#define HAVE_IP(x) ((size_t)(ip_end - ip) >= (size_t)(x)) #define HAVE_IP(t, x) \
#define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x)) (((size_t)(ip_end - ip) >= (size_t)(t + x)) && \
#define NEED_IP(x) if (!HAVE_IP(x)) goto input_overrun (((t + x) >= t) && ((t + x) >= x)))
#define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun
#define TEST_LB(m_pos) if ((m_pos) < out) goto lookbehind_overrun #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, int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
unsigned char *out, size_t *out_len) unsigned char *out, size_t *out_len)
@ -58,14 +78,14 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
while (unlikely(*ip == 0)) { while (unlikely(*ip == 0)) {
t += 255; t += 255;
ip++; ip++;
NEED_IP(1); NEED_IP(1, 0);
} }
t += 15 + *ip++; t += 15 + *ip++;
} }
t += 3; t += 3;
copy_literal_run: copy_literal_run:
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
if (likely(HAVE_IP(t + 15) && HAVE_OP(t + 15))) { if (likely(HAVE_IP(t, 15) && HAVE_OP(t, 15))) {
const unsigned char *ie = ip + t; const unsigned char *ie = ip + t;
unsigned char *oe = op + t; unsigned char *oe = op + t;
do { do {
@ -81,8 +101,8 @@ copy_literal_run:
} else } else
#endif #endif
{ {
NEED_OP(t); NEED_OP(t, 0);
NEED_IP(t + 3); NEED_IP(t, 3);
do { do {
*op++ = *ip++; *op++ = *ip++;
} while (--t > 0); } while (--t > 0);
@ -95,7 +115,7 @@ copy_literal_run:
m_pos -= t >> 2; m_pos -= t >> 2;
m_pos -= *ip++ << 2; m_pos -= *ip++ << 2;
TEST_LB(m_pos); TEST_LB(m_pos);
NEED_OP(2); NEED_OP(2, 0);
op[0] = m_pos[0]; op[0] = m_pos[0];
op[1] = m_pos[1]; op[1] = m_pos[1];
op += 2; op += 2;
@ -119,10 +139,10 @@ copy_literal_run:
while (unlikely(*ip == 0)) { while (unlikely(*ip == 0)) {
t += 255; t += 255;
ip++; ip++;
NEED_IP(1); NEED_IP(1, 0);
} }
t += 31 + *ip++; t += 31 + *ip++;
NEED_IP(2); NEED_IP(2, 0);
} }
m_pos = op - 1; m_pos = op - 1;
next = get_unaligned_le16(ip); next = get_unaligned_le16(ip);
@ -137,10 +157,10 @@ copy_literal_run:
while (unlikely(*ip == 0)) { while (unlikely(*ip == 0)) {
t += 255; t += 255;
ip++; ip++;
NEED_IP(1); NEED_IP(1, 0);
} }
t += 7 + *ip++; t += 7 + *ip++;
NEED_IP(2); NEED_IP(2, 0);
} }
next = get_unaligned_le16(ip); next = get_unaligned_le16(ip);
ip += 2; ip += 2;
@ -154,7 +174,7 @@ copy_literal_run:
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
if (op - m_pos >= 8) { if (op - m_pos >= 8) {
unsigned char *oe = op + t; unsigned char *oe = op + t;
if (likely(HAVE_OP(t + 15))) { if (likely(HAVE_OP(t, 15))) {
do { do {
COPY8(op, m_pos); COPY8(op, m_pos);
op += 8; op += 8;
@ -164,7 +184,7 @@ copy_literal_run:
m_pos += 8; m_pos += 8;
} while (op < oe); } while (op < oe);
op = oe; op = oe;
if (HAVE_IP(6)) { if (HAVE_IP(6, 0)) {
state = next; state = next;
COPY4(op, ip); COPY4(op, ip);
op += next; op += next;
@ -172,7 +192,7 @@ copy_literal_run:
continue; continue;
} }
} else { } else {
NEED_OP(t); NEED_OP(t, 0);
do { do {
*op++ = *m_pos++; *op++ = *m_pos++;
} while (op < oe); } while (op < oe);
@ -181,7 +201,7 @@ copy_literal_run:
#endif #endif
{ {
unsigned char *oe = op + t; unsigned char *oe = op + t;
NEED_OP(t); NEED_OP(t, 0);
op[0] = m_pos[0]; op[0] = m_pos[0];
op[1] = m_pos[1]; op[1] = m_pos[1];
op += 2; op += 2;
@ -194,15 +214,15 @@ match_next:
state = next; state = next;
t = next; t = next;
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
if (likely(HAVE_IP(6) && HAVE_OP(4))) { if (likely(HAVE_IP(6, 0) && HAVE_OP(4, 0))) {
COPY4(op, ip); COPY4(op, ip);
op += t; op += t;
ip += t; ip += t;
} else } else
#endif #endif
{ {
NEED_IP(t + 3); NEED_IP(t, 3);
NEED_OP(t); NEED_OP(t, 0);
while (t > 0) { while (t > 0) {
*op++ = *ip++; *op++ = *ip++;
t--; t--;