CVE-2011-4913: Improper Input Validation (CWE-20)
value: The rose_parse_ccitt function in net/rose/rose_subr.c in the Linux kernel before 2.6.39 does not validate the FAC_CCITT_DEST_NSAP and FAC_CCITT_SRC_NSAP fields, which allows remote attackers to (1) cause a denial of service (integer underflow, heap memory corruption, and panic) via a small length value in data sent to a ROSE socket, or (2) conduct stack-based buffer overflow attacks via a large length value in data sent to a ROSE socket.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Published: 2012-06-21T23:55Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: net/rose/rose_subr.c
Commit date: 2011-03-27 17:59:03 -0700
Commit message: ROSE: prevent heap corruption with bad facilities

When parsing the FAC_NATIONAL_DIGIS facilities field, it's possible for
a remote host to provide more digipeaters than expected, resulting in
heap corruption.  Check against ROSE_MAX_DIGIS to prevent overflows, and
abort facilities parsing on failure.

Additionally, when parsing the FAC_CCITT_DEST_NSAP and
FAC_CCITT_SRC_NSAP facilities fields, a remote host can provide a length
of less than 10, resulting in an underflow in a memcpy size, causing a
kernel panic due to massive heap corruption.  A length of greater than
20 results in a stack overflow of the callsign array.  Abort facilities
parsing on these invalid length values.

Signed-off-by: Dan Rosenberg <drosenberg@vsecurity.com>
Cc: stable@kernel.org
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c
index 1734abba26a29..174d51c9ce377 100644
--- a/net/rose/rose_subr.c
+++ b/net/rose/rose_subr.c
@@ -288,14 +288,19 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct *
 			else if (*p == FAC_NATIONAL_DIGIS) {
 				fac_national_digis_received = 1;
 				facilities->source_ndigis = 0;
 				facilities->dest_ndigis   = 0;
 				for (pt = p + 2, lg = 0 ; lg < l ; pt += AX25_ADDR_LEN, lg += AX25_ADDR_LEN) {
-					if (pt[6] & AX25_HBIT)
+					if (pt[6] & AX25_HBIT) {
+						if (facilities->dest_ndigis >= ROSE_MAX_DIGIS)
+							return -1;
 						memcpy(&facilities->dest_digis[facilities->dest_ndigis++], pt, AX25_ADDR_LEN);
-					else
+					} else {
+						if (facilities->source_ndigis >= ROSE_MAX_DIGIS)
+							return -1;
 						memcpy(&facilities->source_digis[facilities->source_ndigis++], pt, AX25_ADDR_LEN);
+					}
 				}
 			}
 			p   += l + 2;
 			n   += l + 2;
 			len -= l + 2;
@@ -331,10 +336,15 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac
 			len -= 4;
 			break;
 
 		case 0xC0:
 			l = p[1];
+
+			/* Prevent overflows*/
+			if (l < 10 || l > 20)
+				return -1;
+
 			if (*p == FAC_CCITT_DEST_NSAP) {
 				memcpy(&facilities->source_addr, p + 7, ROSE_ADDR_LEN);
 				memcpy(callsign, p + 12,   l - 10);
 				callsign[l - 10] = '\0';
 				asc2ax(&facilities->source_call, callsign);
@@ -371,16 +381,20 @@ int rose_parse_facilities(unsigned char *p,
 			p++;
 
 			switch (*p) {
 			case FAC_NATIONAL:		/* National */
 				len = rose_parse_national(p + 1, facilities, facilities_len - 1);
+				if (len < 0)
+					return 0;
 				facilities_len -= len + 1;
 				p += len + 1;
 				break;
 
 			case FAC_CCITT:		/* CCITT */
 				len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1);
+				if (len < 0)
+					return 0;
 				facilities_len -= len + 1;
 				p += len + 1;
 				break;
 
 			default:

Vulnerable code: 					if (pt[6] & AX25_HBIT)
					else
Fixed code: 					if (pt[6] & AX25_HBIT) {
						if (facilities->dest_ndigis >= ROSE_MAX_DIGIS)
							return -1;
					} else {
						if (facilities->source_ndigis >= ROSE_MAX_DIGIS)
							return -1;
					}

			/* Prevent overflows*/
			if (l < 10 || l > 20)
				return -1;

				if (len < 0)
					return 0;
				if (len < 0)
					return 0;