From 5dc1860d8267b1e0cb78c9ffa2a40bea2fdb3ddc Mon Sep 17 00:00:00 2001
From: Guy Harris <guy@alum.mit.edu>
Date: Wed, 22 Mar 2017 14:09:28 -0700
Subject: [PATCH] CVE-2017-13030/PIM: Redo bounds checks and add length checks.

Use ND_TCHECK macros to do bounds checking, and add length checks before
the bounds checks.

Add a bounds check that the review process found was missing.

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

Update one test output file to reflect the changes.
---
 print-pim.c                        | 383 ++++++++++++++++-------------
 tests/TESTLIST                     |   1 +
 tests/heapoverflow-in_checksum.out |   3 +-
 tests/pim_header_asan.out          |   2 +
 tests/pim_header_asan.pcap         | Bin 0 -> 96 bytes
 5 files changed, 212 insertions(+), 177 deletions(-)
 create mode 100644 tests/pim_header_asan.out
 create mode 100644 tests/pim_header_asan.pcap

Index: tcpdump-3.9.8/print-pim.c
===================================================================
--- tcpdump-3.9.8.orig/print-pim.c
+++ tcpdump-3.9.8/print-pim.c
@@ -149,20 +149,28 @@ pimv1_join_prune_print(register const u_
 		return;
 	}
 
+	if (len < sizeof(struct in_addr))
+		goto trunc;
 	TCHECK2(bp[0], sizeof(struct in_addr));
 	if (vflag > 1)
 		(void)printf("\n");
 	(void)printf(" Upstream Nbr: %s", ipaddr_string(bp));
-	TCHECK2(bp[6], 2);
+	bp += 4;
+	len -= 4;
+	if (len < 4)
+		goto trunc;
+	TCHECK2(bp[2], 2);
 	if (vflag > 1)
 		(void)printf("\n");
 	(void)printf(" Hold time: ");
-	unsigned_relts_print(EXTRACT_16BITS(&bp[6]));
+	unsigned_relts_print(EXTRACT_16BITS(&bp[2]));
 	if (vflag < 2)
 		return;
-	bp += 8;
-	len -= 8;
+	bp += 4;
+	len -= 4;
 
+	if (len < 4)
+		goto trunc;
 	TCHECK2(bp[0], 4);
 	maddrlen = bp[1];
 	addrlen = bp[2];
@@ -174,17 +182,27 @@ pimv1_join_prune_print(register const u_
 		 * XXX - does the address have length "addrlen" and the
 		 * mask length "maddrlen"?
 		 */
+		if (len < 4)
+			goto trunc;
 		TCHECK2(bp[0], sizeof(struct in_addr));
 		(void)printf("\n\tGroup: %s", ipaddr_string(bp));
-		TCHECK2(bp[4], sizeof(struct in_addr));
-		if (EXTRACT_32BITS(&bp[4]) != 0xffffffff)
-			(void)printf("/%s", ipaddr_string(&bp[4]));
-		TCHECK2(bp[8], 4);
-		njoin = EXTRACT_16BITS(&bp[8]);
-		nprune = EXTRACT_16BITS(&bp[10]);
+		bp += 4;
+		len -= 4;
+		if (len < 4)
+			goto trunc;
+		TCHECK2(bp[0], sizeof(struct in_addr));
+		if (EXTRACT_32BITS(&bp[0]) != 0xffffffff)
+			printf("/%s", ipaddr_string(&bp[0]));
+		bp += 4;
+		len -= 4;
+		if (len < 4)
+			goto trunc;
+		TCHECK2(bp[0], 4);
+		njoin = EXTRACT_16BITS(&bp[0]);
+		nprune = EXTRACT_16BITS(&bp[2]);
 		(void)printf(" joined: %d pruned: %d", njoin, nprune);
-		bp += 12;
-		len -= 12;
+		bp += 4;
+		len -= 4;
 		for (njp = 0; njp < (njoin + nprune); njp++) {
 			const char *type;
 
@@ -192,12 +210,14 @@ pimv1_join_prune_print(register const u_
 				type = "Join ";
 			else
 				type = "Prune";
+			if (len < 6)
+				goto trunc;
 			TCHECK2(bp[0], 6);
 			(void)printf("\n\t%s %s%s%s%s/%d", type,
 			    (bp[0] & 0x01) ? "Sparse " : "Dense ",
 			    (bp[1] & 0x80) ? "WC " : "",
 			    (bp[1] & 0x40) ? "RP " : "SPT ",
-			ipaddr_string(&bp[2]), bp[1] & 0x3f);
+			    ipaddr_string(&bp[2]), bp[1] & 0x3f);
 			bp += 6;
 			len -= 6;
 		}
@@ -211,13 +231,8 @@ trunc:
 void
 pimv1_print(register const u_char *bp, register u_int len)
 {
-	register const u_char *ep;
 	register u_char type;
 
-	ep = (const u_char *)snapend;
-	if (bp >= ep)
-		return;
-
 	TCHECK(bp[1]);
 	type = bp[1];
 
@@ -297,8 +312,11 @@ pimv1_print(register const u_char *bp, r
 		break;
 	case 7:
 		(void)printf(" Graft-ACK");
-		if (vflag)
+		if (vflag) {
+			if (len < 8)
+				goto trunc;
 			pimv1_join_prune_print(&bp[8], len - 8);
+		}
 		break;
 	case 8:
 		(void)printf(" Mode");
@@ -330,6 +348,8 @@ cisco_autorp_print(register const u_char
 	int numrps;
 	int hold;
 
+	if (len < 8)
+		goto trunc;
 	TCHECK(bp[0]);
 	(void)printf(" auto-rp ");
 	type = bp[0];
@@ -377,10 +397,16 @@ cisco_autorp_print(register const u_char
 		int nentries;
 		char s;
 
+		if (len < 4)
+			goto trunc;
 		TCHECK2(bp[0], 4);
 		(void)printf(" RP %s", ipaddr_string(bp));
-		TCHECK(bp[4]);
-		switch (bp[4] & 0x3) {
+		bp += 4;
+		len -= 4;
+		if (len < 1)
+			goto trunc;
+		TCHECK(bp[0]);
+		switch (bp[0] & 0x3) {
 		case 0: printf(" PIMv?");
 			break;
 		case 1:	printf(" PIMv1");
@@ -390,13 +416,20 @@ cisco_autorp_print(register const u_char
 		case 3:	printf(" PIMv1+2");
 			break;
 		}
-		if (bp[4] & 0xfc)
-			(void)printf(" [rsvd=0x%02x]", bp[4] & 0xfc);
-		TCHECK(bp[5]);
-		nentries = bp[5];
-		bp += 6; len -= 6;
+		if (bp[0] & 0xfc)
+			printf(" [rsvd=0x%02x]", bp[0] & 0xfc);
+		bp += 1;
+		len -= 1;
+		if (len < 1)
+			goto trunc;
+		TCHECK(bp[0]);
+		nentries = bp[0];
+		bp += 1;
+		len -= 1;
 		s = ' ';
 		for (; nentries; nentries--) {
+			if (len < 6)
+				goto trunc;
 			TCHECK2(bp[0], 6);
 			(void)printf("%c%s%s/%d", s, bp[0] & 1 ? "!" : "",
 			    ipaddr_string(&bp[2]), bp[1]);
@@ -416,16 +449,13 @@ trunc:
 void
 pim_print(register const u_char *bp, register u_int len)
 {
-	register const u_char *ep;
 	register struct pim *pim = (struct pim *)bp;
 
-	ep = (const u_char *)snapend;
-	if (bp >= ep)
-		return;
 #ifdef notyet			/* currently we see only version and type */
 	TCHECK(pim->pim_rsv);
 #endif
 
+	TCHECK(pim->pim_typever);
 	switch (PIM_VER(pim->pim_typever)) {
 	case 2:
             if (!vflag) {
@@ -449,6 +479,10 @@ pim_print(register const u_char *bp, reg
 		break;
 	}
 	return;
+
+trunc:
+	printf("[|pim]");
+	return;
 }
 
 /*
@@ -491,8 +525,6 @@ pim_print(register const u_char *bp, reg
  *
  */
 
-static int pimv2_addr_len;
-
 enum pimv2_addrtype {
 	pimv2_unicast, pimv2_group, pimv2_source
 };
@@ -518,24 +550,24 @@ enum pimv2_addrtype {
  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  */
 static int
-pimv2_addr_print(const u_char *bp, enum pimv2_addrtype at, int silent)
+pimv2_addr_print(const u_char *bp, u_int len, enum pimv2_addrtype at, u_int addr_len, int silent)
 {
 	int af;
-	int len, hdrlen;
-
-	TCHECK(bp[0]);
+	int hdrlen;
 
-	if (pimv2_addr_len == 0) {
+	if (addr_len == 0) {
+		if (len < 2)
+			goto trunc;
 		TCHECK(bp[1]);
 		switch (bp[0]) {
 		case 1:
 			af = AF_INET;
-			len = sizeof(struct in_addr);
+			addr_len = (u_int)sizeof(struct in_addr);
 			break;
 #ifdef INET6
 		case 2:
 			af = AF_INET6;
-			len = sizeof(struct in6_addr);
+		        addr_len = (u_int)sizeof(struct in6_addr);
 			break;
 #endif
 		default:
@@ -545,7 +577,7 @@ pimv2_addr_print(const u_char *bp, enum
 			return -1;
 		hdrlen = 2;
 	} else {
-		switch (pimv2_addr_len) {
+		switch (addr_len) {
 		case sizeof(struct in_addr):
 			af = AF_INET;
 			break;
@@ -558,14 +590,16 @@ pimv2_addr_print(const u_char *bp, enum
 			return -1;
 			break;
 		}
-		len = pimv2_addr_len;
 		hdrlen = 0;
 	}
 
 	bp += hdrlen;
+	len -= hdrlen;
 	switch (at) {
 	case pimv2_unicast:
-		TCHECK2(bp[0], len);
+		if (len < addr_len)
+			goto trunc;
+		TCHECK2(bp[0], addr_len);
 		if (af == AF_INET) {
 			if (!silent)
 				(void)printf("%s", ipaddr_string(bp));
@@ -576,10 +610,12 @@ pimv2_addr_print(const u_char *bp, enum
 				(void)printf("%s", ip6addr_string(bp));
 		}
 #endif
-		return hdrlen + len;
+		return hdrlen + addr_len;
 	case pimv2_group:
 	case pimv2_source:
-		TCHECK2(bp[0], len + 2);
+		if (len < addr_len + 2)
+			goto trunc;
+		TCHECK2(bp[0], addr_len + 2);
 		if (af == AF_INET) {
 			if (!silent) {
 				(void)printf("%s", ipaddr_string(bp + 2));
@@ -610,7 +646,7 @@ pimv2_addr_print(const u_char *bp, enum
 				(void)printf(")");
 			}
 		}
-		return hdrlen + 2 + len;
+		return hdrlen + 2 + addr_len;
 	default:
 		return -1;
 	}
@@ -624,17 +660,23 @@ pimv2_print(register const u_char *bp, r
 	register const u_char *ep;
 	register struct pim *pim = (struct pim *)bp;
 	int advance;
+	int pimv2_addr_len;
 
 	ep = (const u_char *)snapend;
 	if (bp >= ep)
 		return;
 	if (ep > bp + len)
 		ep = bp + len;
+	if (len < 2)
+		goto trunc;
 	TCHECK(pim->pim_rsv);
 	pimv2_addr_len = pim->pim_rsv;
 	if (pimv2_addr_len != 0)
 		(void)printf(", RFC2117-encoding");
 
+	if (len < 4)
+		goto trunc;
+	TCHECK(pim->pim_cksum);
         printf(", cksum 0x%04x ", EXTRACT_16BITS(&pim->pim_cksum));
         if (EXTRACT_16BITS(&pim->pim_cksum) == 0) {
                 printf("(unverified)");
@@ -643,24 +685,29 @@ pimv2_print(register const u_char *bp, r
                        TTEST2(bp[0], len) &&
                        in_cksum((const u_short*)bp, len, 0) ? "in" : "" );
         }
+	bp += 4;
+	len -= 4;
 
 	switch (PIM_TYPE(pim->pim_typever)) {
 	case PIMV2_TYPE_HELLO:
 	    {
 		u_int16_t otype, olen;
-		bp += 4;
-		while (bp < ep) {
+		while (len > 0) {
+			if (len < 4)
+				goto trunc;
 			TCHECK2(bp[0], 4);
 			otype = EXTRACT_16BITS(&bp[0]);
 			olen = EXTRACT_16BITS(&bp[2]);
-			TCHECK2(bp[0], 4 + olen);
-
                         printf("\n\t  %s Option (%u), length %u, Value: ",
                                tok2str( pimv2_hello_option_values,"Unknown",otype),
                                otype,
                                olen);
 			bp += 4;
+			len -= 4;
 
+			if (len < olen)
+				goto trunc;
+			TCHECK2(bp[0], olen);
 			switch (otype) {
 			case PIMV2_HELLO_OPTION_HOLDTIME:
 				if (olen != 2) {
@@ -730,16 +777,15 @@ pimv2_print(register const u_char *bp, r
                         case PIMV2_HELLO_OPTION_ADDRESS_LIST:
 				if (vflag > 1) {
 					const u_char *ptr = bp;
+					u_int plen = len;
 					while (ptr < (bp+olen)) {
 						int advance;
-
 						printf("\n\t    ");
-						advance = pimv2_addr_print(ptr, pimv2_unicast, 0);
-						if (advance < 0) {
-							printf("...");
-							break;
-						}
+						advance = pimv2_addr_print(ptr, plen, pimv2_unicast, pimv2_addr_len, 0);
+						if (advance < 0)
+							goto trunc;
 						ptr += advance;
+						plen -= advance;
 					}
 				}
 				break;
@@ -752,6 +798,7 @@ pimv2_print(register const u_char *bp, r
                         if (vflag> 1)
                             print_unknown_data(bp,"\n\t    ",olen);
 			bp += olen;
+			len -= olen;
 		}
 		break;
 	    }
@@ -760,19 +807,24 @@ pimv2_print(register const u_char *bp, r
 	{
 		struct ip *ip;
 
-                if (!TTEST2(*(bp+4), PIMV2_REGISTER_FLAG_LEN))
-                        goto trunc;
+		if (len < 4)
+			goto trunc;
+		TCHECK2(*bp, PIMV2_REGISTER_FLAG_LEN);
 
                 printf(", Flags [ %s ]\n\t",
                        tok2str(pimv2_register_flag_values,
                                "none",
                                EXTRACT_32BITS(bp+4)));
 
-		bp += 8; len -= 8;
+		bp += 4; len -= 4;
 		/* encapsulated multicast packet */
+		if (len == 0)
+			goto trunc;
 		ip = (struct ip *)bp;
+		TCHECK(ip->ip_vhl);
 		switch (IP_V(ip)) {
                 case 0: /* Null header */
+			TCHECK(ip->ip_dst);
 			(void)printf("IP-Null-header %s > %s",
                                      ipaddr_string(&ip->ip_src),
                                      ipaddr_string(&ip->ip_dst));
@@ -794,22 +846,13 @@ pimv2_print(register const u_char *bp, r
 	}
 
 	case PIMV2_TYPE_REGISTER_STOP:
-		bp += 4; len -= 4;
-		if (bp >= ep)
-			break;
 		(void)printf(" group=");
-		if ((advance = pimv2_addr_print(bp, pimv2_group, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance; len -= advance;
-		if (bp >= ep)
-			break;
 		(void)printf(" source=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance; len -= advance;
 		break;
 
@@ -860,19 +903,15 @@ pimv2_print(register const u_char *bp, r
 		u_int16_t nprune;
 		int i, j;
 
-		bp += 4; len -= 4;
 		if (PIM_TYPE(pim->pim_typever) != 7) {	/*not for Graft-ACK*/
-			if (bp >= ep)
-				break;
 			(void)printf(", upstream-neighbor: ");
-			if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-				(void)printf("...");
-				break;
-			}
+			if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+				goto trunc;
 			bp += advance; len -= advance;
 		}
-		if (bp + 4 > ep)
-			break;
+		if (len < 4)
+			goto trunc;
+		TCHECK2(*bp, 4);
 		ngroup = bp[1];
 		holdtime = EXTRACT_16BITS(&bp[2]);
 		(void)printf("\n\t  %u group(s)", ngroup);
@@ -885,139 +924,125 @@ pimv2_print(register const u_char *bp, r
 		}
 		bp += 4; len -= 4;
 		for (i = 0; i < ngroup; i++) {
-			if (bp >= ep)
-				goto jp_done;
 			(void)printf("\n\t    group #%u: ", i+1);
-			if ((advance = pimv2_addr_print(bp, pimv2_group, 0)) < 0) {
-				(void)printf("...)");
-				goto jp_done;
-			}
+			if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+				goto trunc;
 			bp += advance; len -= advance;
-			if (bp + 4 > ep) {
-				(void)printf("...)");
-				goto jp_done;
-			}
+			if (len < 4)
+				goto trunc;
+			TCHECK2(*bp, 4);
 			njoin = EXTRACT_16BITS(&bp[0]);
 			nprune = EXTRACT_16BITS(&bp[2]);
 			(void)printf(", joined sources: %u, pruned sources: %u", njoin,nprune);
 			bp += 4; len -= 4;
 			for (j = 0; j < njoin; j++) {
 				(void)printf("\n\t      joined source #%u: ",j+1);
-				if ((advance = pimv2_addr_print(bp, pimv2_source, 0)) < 0) {
-					(void)printf("...)");
-					goto jp_done;
-				}
+				if ((advance = pimv2_addr_print(bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
+					goto trunc;
 				bp += advance; len -= advance;
 			}
 			for (j = 0; j < nprune; j++) {
 				(void)printf("\n\t      pruned source #%u: ",j+1);
-				if ((advance = pimv2_addr_print(bp, pimv2_source, 0)) < 0) {
-					(void)printf("...)");
-					goto jp_done;
-				}
+				if ((advance = pimv2_addr_print(bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
+					goto trunc;
 				bp += advance; len -= advance;
 			}
 		}
-	jp_done:
 		break;
 	    }
 
 	case PIMV2_TYPE_BOOTSTRAP:
 	{
 		int i, j, frpcnt;
-		bp += 4;
 
 		/* Fragment Tag, Hash Mask len, and BSR-priority */
-		if (bp + sizeof(u_int16_t) >= ep) break;
+		if (len < 2)
+			goto trunc;
+		TCHECK_16BITS(bp);
 		(void)printf(" tag=%x", EXTRACT_16BITS(bp));
-		bp += sizeof(u_int16_t);
-		if (bp >= ep) break;
+		bp += 2;
+		len -= 2;
+		if (len < 1)
+			goto trunc;
+		TCHECK(bp[0]);
 		(void)printf(" hashmlen=%d", bp[0]);
-		if (bp + 1 >= ep) break;
+		if (len < 2)
+			goto trunc;
+		TCHECK(bp[2]);
 		(void)printf(" BSRprio=%d", bp[1]);
 		bp += 2;
+		len -= 2;
 
 		/* Encoded-Unicast-BSR-Address */
-		if (bp >= ep) break;
 		(void)printf(" BSR=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance;
+		len -= advance;
 
 		for (i = 0; bp < ep; i++) {
 			/* Encoded-Group Address */
 			(void)printf(" (group%d: ", i);
-			if ((advance = pimv2_addr_print(bp, pimv2_group, 0))
-			    < 0) {
-				(void)printf("...)");
-				goto bs_done;
-			}
+			if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+				goto trunc;
 			bp += advance;
+			len -= advance;
 
 			/* RP-Count, Frag RP-Cnt, and rsvd */
-			if (bp >= ep) {
-				(void)printf("...)");
-				goto bs_done;
-			}
+			if (len < 1)
+				goto trunc;
+			TCHECK(bp[0]);
 			(void)printf(" RPcnt=%d", bp[0]);
-			if (bp + 1 >= ep) {
-				(void)printf("...)");
-				goto bs_done;
-			}
+			if (len < 2)
+				goto trunc;
+			TCHECK(bp[1]);
 			(void)printf(" FRPcnt=%d", frpcnt = bp[1]);
+			if (len < 4)
+				goto trunc;
 			bp += 4;
+			len -= 4;
 
 			for (j = 0; j < frpcnt && bp < ep; j++) {
 				/* each RP info */
 				(void)printf(" RP%d=", j);
-				if ((advance = pimv2_addr_print(bp,
+				if ((advance = pimv2_addr_print(bp, len,
 								pimv2_unicast,
-								0)) < 0) {
-					(void)printf("...)");
-					goto bs_done;
-				}
+								pimv2_addr_len,
+								0)) < 0)
+					goto trunc;
 				bp += advance;
+				len -= advance;
 
-				if (bp + 1 >= ep) {
-					(void)printf("...)");
-					goto bs_done;
-				}
+				if (len < 2)
+					goto trunc;
+				TCHECK_16BITS(bp);
 				(void)printf(",holdtime=");
 				unsigned_relts_print(EXTRACT_16BITS(bp));
-				if (bp + 2 >= ep) {
-					(void)printf("...)");
-					goto bs_done;
-				}
+				if (len < 3)
+					goto trunc;
+				TCHECK(bp[2]);
 				(void)printf(",prio=%d", bp[2]);
+				if (len < 4)
+					goto trunc;
 				bp += 4;
+				len -= 4;
 			}
 			(void)printf(")");
 		}
-	   bs_done:
 		break;
 	}
 	case PIMV2_TYPE_ASSERT:
-		bp += 4; len -= 4;
-		if (bp >= ep)
-			break;
 		(void)printf(" group=");
-		if ((advance = pimv2_addr_print(bp, pimv2_group, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance; len -= advance;
-		if (bp >= ep)
-			break;
 		(void)printf(" src=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance; len -= advance;
-		if (bp + 8 > ep)
-			break;
+		if (len < 8)
+			goto trunc;
+		TCHECK2(*bp, 8);
 		if (bp[0] & 0x80)
 			(void)printf(" RPT");
 		(void)printf(" pref=%u", EXTRACT_32BITS(&bp[0]) & 0x7fffffff);
@@ -1027,61 +1052,62 @@ pimv2_print(register const u_char *bp, r
 	case PIMV2_TYPE_CANDIDATE_RP:
 	{
 		int i, pfxcnt;
-		bp += 4;
 
 		/* Prefix-Cnt, Priority, and Holdtime */
-		if (bp >= ep) break;
+		if (len < 1)
+			goto trunc;
+		TCHECK(bp[0]);
 		(void)printf(" prefix-cnt=%d", bp[0]);
 		pfxcnt = bp[0];
-		if (bp + 1 >= ep) break;
+		if (len < 2)
+			goto trunc;
+		TCHECK(bp[1]);
 		(void)printf(" prio=%d", bp[1]);
-		if (bp + 3 >= ep) break;
+		if (len < 4)
+			goto trunc;
+		TCHECK_16BITS(&bp[2]);
 		(void)printf(" holdtime=");
 		unsigned_relts_print(EXTRACT_16BITS(&bp[2]));
 		bp += 4;
+		len -= 4;
 
 		/* Encoded-Unicast-RP-Address */
-		if (bp >= ep) break;
 		(void)printf(" RP=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance;
+		len -= advance;
 
 		/* Encoded-Group Addresses */
 		for (i = 0; i < pfxcnt && bp < ep; i++) {
 			(void)printf(" Group%d=", i);
-			if ((advance = pimv2_addr_print(bp, pimv2_group, 0))
-			    < 0) {
-				(void)printf("...");
-				break;
-			}
+			if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+				goto trunc;
 			bp += advance;
+			len -= advance;
 		}
 		break;
 	}
 
 	case PIMV2_TYPE_PRUNE_REFRESH:
 		(void)printf(" src=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance;
+		len -= advance;
 		(void)printf(" grp=");
-		if ((advance = pimv2_addr_print(bp, pimv2_group, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance;
+		len -= advance;
 		(void)printf(" forwarder=");
-		if ((advance = pimv2_addr_print(bp, pimv2_unicast, 0)) < 0) {
-			(void)printf("...");
-			break;
-		}
+		if ((advance = pimv2_addr_print(bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+			goto trunc;
 		bp += advance;
-		TCHECK2(bp[0], 2);
+		len -= advance;
+		if (len < 2)
+			goto trunc;
+		TCHECK_16BITS(bp);
 		(void)printf(" TUNR ");
 		unsigned_relts_print(EXTRACT_16BITS(bp));
 		break;
