commit 237efcf593ee369519e9dfdc9166702219dabfec
Author: Guy Harris <guy@alum.mit.edu>
Date:   Fri Jul 3 15:16:20 2015 -0700

    CVE-2016-7939/Add some bounds checks.
    
    Fixes heap overflows found with American Fuzzy Lop by Hanno Böck.

Index: tcpdump-3.9.8/print-gre.c
===================================================================
--- tcpdump-3.9.8.orig/print-gre.c
+++ tcpdump-3.9.8/print-gre.c
@@ -84,19 +84,18 @@ struct tok gre_flag_values[] = {
 
 void gre_print_0(const u_char *, u_int);
 void gre_print_1(const u_char *, u_int);
-void gre_sre_print(u_int16_t, u_int8_t, u_int8_t, const u_char *, u_int);
-void gre_sre_ip_print(u_int8_t, u_int8_t, const u_char *, u_int);
-void gre_sre_asn_print(u_int8_t, u_int8_t, const u_char *, u_int);
+static int gre_sre_print(u_int16_t, u_int8_t, u_int8_t, const u_char *, u_int);
+static int gre_sre_ip_print(u_int8_t, u_int8_t, const u_char *, u_int);
+static int gre_sre_asn_print(u_int8_t, u_int8_t, const u_char *, u_int);
 
 void
 gre_print(const u_char *bp, u_int length)
 {
 	u_int len = length, vers;
 
-	if (len < 2) {
-		printf("[|gre]");
-		return;
-	}
+	TCHECK2(*bp, 2);
+	if (len < 2)
+	  goto trunc; 
 	vers = EXTRACT_16BITS(bp) & GRE_VERS_MASK;
         printf("GREv%u",vers);
 
@@ -113,6 +112,9 @@ gre_print(const u_char *bp, u_int length
         }
 	return;
 
+ trunc:
+	printf("%s", "[|gre]");
+	return;
 }
 
 void
@@ -129,6 +131,7 @@ gre_print_0(const u_char *bp, u_int leng
 	len -= 2;
 	bp += 2;
 
+	TCHECK2(*bp, 2);
 	if (len < 2)
 		goto trunc;
 	prot = EXTRACT_16BITS(bp);
@@ -136,6 +139,7 @@ gre_print_0(const u_char *bp, u_int leng
 	bp += 2;
 
 	if ((flags & GRE_CP) | (flags & GRE_RP)) {
+		TCHECK2(*bp, 2);
 		if (len < 2)
 			goto trunc;
 		if (vflag)
@@ -143,6 +147,7 @@ gre_print_0(const u_char *bp, u_int leng
 		bp += 2;
 		len -= 2;
 
+		TCHECK2(*bp, 2);
 		if (len < 2)
 			goto trunc;
 		printf(", off 0x%x", EXTRACT_16BITS(bp));
@@ -151,6 +156,7 @@ gre_print_0(const u_char *bp, u_int leng
 	}
 
 	if (flags & GRE_KP) {
+		TCHECK2(*bp, 4);
 		if (len < 4)
 			goto trunc;
 		printf(", key=0x%x", EXTRACT_32BITS(bp));
@@ -159,6 +165,7 @@ gre_print_0(const u_char *bp, u_int leng
 	}
 
 	if (flags & GRE_SP) {
+		TCHECK2(*bp, 4);
 		if (len < 4)
 			goto trunc;
 		printf(", seq %u", EXTRACT_32BITS(bp));
@@ -172,6 +179,7 @@ gre_print_0(const u_char *bp, u_int leng
 			u_int8_t sreoff;
 			u_int8_t srelen;
 
+			TCHECK2(*bp, 4);
 			if (len < 4)
 				goto trunc;
 			af = EXTRACT_16BITS(bp);
@@ -183,7 +191,8 @@ gre_print_0(const u_char *bp, u_int leng
 			if (af == 0 && srelen == 0)
 				break;
 
-			gre_sre_print(af, sreoff, srelen, bp, len);
+			if (!gre_sre_print(ndo, af, sreoff, srelen, bp, len))
+				goto trunc;
 
 			if (len < srelen)
 				goto trunc;
@@ -248,6 +257,7 @@ gre_print_1(const u_char *bp, u_int leng
             printf(", Flags [%s]",
                    bittok2str(gre_flag_values,"none",flags));
 
+	TCHECK2(*bp, 2);
 	if (len < 2)
 		goto trunc;
 	prot = EXTRACT_16BITS(bp);
@@ -258,6 +268,7 @@ gre_print_1(const u_char *bp, u_int leng
 	if (flags & GRE_KP) {
 		u_int32_t k;
 
+		TCHECK2(*bp, 4);
 		if (len < 4)
 			goto trunc;
 		k = EXTRACT_32BITS(bp);
@@ -267,6 +278,7 @@ gre_print_1(const u_char *bp, u_int leng
 	}
 
 	if (flags & GRE_SP) {
+		TCHECK2(*bp, 4);
 		if (len < 4)
 			goto trunc;
 		printf(", seq %u", EXTRACT_32BITS(bp));
@@ -275,6 +287,7 @@ gre_print_1(const u_char *bp, u_int leng
 	}
 
 	if (flags & GRE_AP) {
+		TCHECK2(*bp, 4);
 		if (len < 4)
 			goto trunc;
 		printf(", ack %u", EXTRACT_32BITS(bp));
@@ -314,26 +327,31 @@ trunc:
 	printf("[|gre]");
 }
 
-void
+static int
 gre_sre_print(u_int16_t af, u_int8_t sreoff, u_int8_t srelen,
     const u_char *bp, u_int len)
 {
+	int ret;
+
 	switch (af) {
 	case GRESRE_IP:
 		printf(", (rtaf=ip");
-		gre_sre_ip_print(sreoff, srelen, bp, len);
+		ret = gre_sre_ip_print(ndo, sreoff, srelen, bp, len);
 		printf(") ");
 		break;
 	case GRESRE_ASN:
 		printf(", (rtaf=asn");
-		gre_sre_asn_print(sreoff, srelen, bp, len);
+		ret = gre_sre_asn_print(ndo, sreoff, srelen, bp, len);
 		printf(") ");
 		break;
 	default:
 		printf(", (rtaf=0x%x) ", af);
+		ret = 1;
 	}
+	return (ret);
 }
-void
+
+static int
 gre_sre_ip_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len)
 {
 	struct in_addr a;
@@ -341,20 +359,22 @@ gre_sre_ip_print(u_int8_t sreoff, u_int8
 
 	if (sreoff & 3) {
 		printf(", badoffset=%u", sreoff);
-		return;
+		return (1);
 	}
 	if (srelen & 3) {
 		printf(", badlength=%u", srelen);
-		return;
+		return (1);
 	}
 	if (sreoff >= srelen) {
 		printf(", badoff/len=%u/%u", sreoff, srelen);
-		return;
+		return (1);
 	}
 
-	for (;;) {
-		if (len < 4 || srelen == 0)
-			return;
+	while (srelen != 0) {
+		if (!TTEST2(*bp, sizeof(struct in_addr)))
+			return (0);
+		if (len < 4)
+			return (0);
 
 		memcpy(&a, bp, sizeof(a));
 		printf(" %s%s",
@@ -365,29 +385,32 @@ gre_sre_ip_print(u_int8_t sreoff, u_int8
 		len -= 4;
 		srelen -= 4;
 	}
+	return (1);
 }
 
-void
+static int
 gre_sre_asn_print(u_int8_t sreoff, u_int8_t srelen, const u_char *bp, u_int len)
 {
 	const u_char *up = bp;
 
 	if (sreoff & 1) {
 		printf(", badoffset=%u", sreoff);
-		return;
+		return (1);
 	}
 	if (srelen & 1) {
 		printf(", badlength=%u", srelen);
-		return;
+		return (1);
 	}
 	if (sreoff >= srelen) {
 		printf(", badoff/len=%u/%u", sreoff, srelen);
-		return;
+		return (1);
 	}
 
-	for (;;) {
-		if (len < 2 || srelen == 0)
-			return;
+	while (srelen != 0) {
+		if (!TTEST2(*bp, 2))
+			return (0);
+		if (len < 2)
+			return (0);
 
 		printf(" %s%x",
 		    ((bp - up) == sreoff) ? "*" : "",
@@ -397,4 +420,5 @@ gre_sre_asn_print(u_int8_t sreoff, u_int
 		len -= 2;
 		srelen -= 2;
 	}
+	return (1);
 }
