Vulnerability: ** DISPUTED ** Multiple integer overflows in the lzo1x_decompress_safe function in lib/lzo/lzo1x_decompress_safe.c in the LZO decompressor in the Linux kernel before 3.15.2 allow context-dependent attackers to cause a denial of service (memory corruption) via a crafted Literal Run.  NOTE: the author of the LZO algorithms says "the Linux kernel is *not* affected; media hype."
Vulnerability ID: CVE-2014-4608
CWE: CWE-190
Severity score: 6.4
Project: https://github.com/torvalds/linux
Published: 2014-07-03
File: lib/lzo/lzo1x_decompress_safe.c
Commit message: 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>
Commit author: Greg Kroah-Hartman

Vulnerable version (before fix):
 #include <linux/lzo.h> #include "lzodefs.h"  #define HAVE_IP(x)      ((size_t)(ip_end - ip) >= (size_t)(x)) #define HAVE_OP(x)      ((size_t)(op_end - op) >= (size_t)(x)) #define NEED_IP(x)      if (!HAVE_IP(x)) goto input_overrun #define NEED_OP(x)      if (!HAVE_OP(x)) goto output_overrun #define TEST_LB(m_pos)  if ((m_pos) < out) goto lookbehind_overrun  int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 			  unsigned char *out, size_t *out_len) int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 					while (unlikely(*ip == 0)) { 						t += 255; 						ip++; 						NEED_IP(1); 					} 					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 { int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				} else #endif 				{ 					NEED_OP(t); 					NEED_IP(t + 3); 					do { 						*op++ = *ip++; 					} while (--t > 0); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				m_pos -= t >> 2; 				m_pos -= *ip++ << 2; 				TEST_LB(m_pos); 				NEED_OP(2); 				op[0] = m_pos[0]; 				op[1] = m_pos[1]; 				op += 2; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				while (unlikely(*ip == 0)) { 					t += 255; 					ip++; 					NEED_IP(1); 				} 				t += 31 + *ip++; 				NEED_IP(2); 			} 			m_pos = op - 1; 			next = get_unaligned_le16(ip); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				while (unlikely(*ip == 0)) { 					t += 255; 					ip++; 					NEED_IP(1); 				} 				t += 7 + *ip++; 				NEED_IP(2); 			} 			next = get_unaligned_le16(ip); 			ip += 2; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, #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; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 					m_pos += 8; 				} while (op < oe); 				op = oe; 				if (HAVE_IP(6)) { 					state = next; 					COPY4(op, ip); 					op += next; 					ip += next; 					continue; 				} 			} else { 				NEED_OP(t); 				do { 					*op++ = *m_pos++; 				} while (op < oe); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, #endif 		{ 			unsigned char *oe = op + t; 			NEED_OP(t); 			op[0] = m_pos[0]; 			op[1] = m_pos[1]; 			op += 2; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 		state = next; 		t = next; #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) 		if (likely(HAVE_IP(6) && HAVE_OP(4))) { 			COPY4(op, ip); 			op += t; 			ip += t; 		} else #endif 		{ 			NEED_IP(t + 3); 			NEED_OP(t); 			while (t > 0) { 				*op++ = *ip++; 				t--;

Fixed version (after fix):
 #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) int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 					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 { int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				} else #endif 				{ 					NEED_OP(t, 0); 					NEED_IP(t, 3); 					do { 						*op++ = *ip++; 					} while (--t > 0); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				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; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				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); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 				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; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, #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; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 					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); int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, #endif 		{ 			unsigned char *oe = op + t; 			NEED_OP(t, 0); 			op[0] = m_pos[0]; 			op[1] = m_pos[1]; 			op += 2; int lzo1x_decompress_safe(const unsigned char *in, size_t in_len, 		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--;

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