CVE-2017-13019: Out-of-bounds Read (CWE-125)
value: The PGM parser in tcpdump before 4.9.2 has a buffer over-read in print-pgm.c:pgm_print().
CWE description: The product reads data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2017-09-14T06:29Z
Repository: https://github.com/the-tcpdump-group/tcpdump
Language: Other
Files changed: print-pgm.c, tests/TESTLIST, tests/pgm_opts_asan_2.out
Commit date: 2017-09-13 12:25:44 +0100
Commit message: CVE-2017-13019: Clean up PGM option processing.

Add #defines for option lengths or the lengths of the fixed-length part
of the option.  Sometimes those #defines differ from what was there
before; what was there before was wrong, probably because the option
lengths given in RFC 3208 were sometimes wrong - some lengths included
the length of the option header, some lengths didn't.

Don't use "sizeof(uintXX_t)" for sizes in the packet, just use the
number of bytes directly.

For the options that include an IPv4 or IPv6 address, check the option
length against the length of what precedes the address before fetching
any of that data.

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.

Diff: diff --git a/print-pgm.c b/print-pgm.c
index 9bd6eac535..c22b188b7c 100644
--- a/print-pgm.c
+++ b/print-pgm.c
@@ -477,284 +477,334 @@ pgm_print(netdissect_options *ndo,
 		    return;
 		}
 
 		switch (opt_type & PGM_OPT_MASK) {
 		case PGM_OPT_LENGTH:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != 4]", opt_len));
+#define PGM_OPT_LENGTH_LEN	(2+2)
+		    if (opt_len != PGM_OPT_LENGTH_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != %u]",
+			    opt_len, PGM_OPT_LENGTH_LEN));
 			return;
 		    }
 		    ND_PRINT((ndo, " OPTS LEN (extra?) %d", EXTRACT_16BITS(bp)));
-		    bp += sizeof(uint16_t);
-		    opts_len -= 4;
+		    bp += 2;
+		    opts_len -= PGM_OPT_LENGTH_LEN;
 		    break;
 
 		case PGM_OPT_FRAGMENT:
-		    if (opt_len != 16) {
-			ND_PRINT((ndo, "[Bad OPT_FRAGMENT option, length %u != 16]", opt_len));
+#define PGM_OPT_FRAGMENT_LEN	(2+2+4+4+4)
+		    if (opt_len != PGM_OPT_FRAGMENT_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_FRAGMENT option, length %u != %u]",
+			    opt_len, PGM_OPT_FRAGMENT_LEN));
 			return;
 		    }
 		    bp += 2;
 		    seq = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    offset = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    len = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " FRAG seq %u off %u len %u", seq, offset, len));
-		    opts_len -= 16;
+		    opts_len -= PGM_OPT_FRAGMENT_LEN;
 		    break;
 
 		case PGM_OPT_NAK_LIST:
 		    bp += 2;
-		    opt_len -= sizeof(uint32_t);	/* option header */
+		    opt_len -= 4;	/* option header */
 		    ND_PRINT((ndo, " NAK LIST"));
 		    while (opt_len) {
-			if (opt_len < sizeof(uint32_t)) {
+			if (opt_len < 4) {
 			    ND_PRINT((ndo, "[Option length not a multiple of 4]"));
 			    return;
 			}
-			ND_TCHECK2(*bp, sizeof(uint32_t));
+			ND_TCHECK2(*bp, 4);
 			ND_PRINT((ndo, " %u", EXTRACT_32BITS(bp)));
-			bp += sizeof(uint32_t);
-			opt_len -= sizeof(uint32_t);
-			opts_len -= sizeof(uint32_t);
+			bp += 4;
+			opt_len -= 4;
+			opts_len -= 4;
 		    }
 		    break;
 
 		case PGM_OPT_JOIN:
-		    if (opt_len != 8) {
-			ND_PRINT((ndo, "[Bad OPT_JOIN option, length %u != 8]", opt_len));
+#define PGM_OPT_JOIN_LEN	(2+2+4)
+		    if (opt_len != PGM_OPT_JOIN_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_JOIN option, length %u != %u]",
+			    opt_len, PGM_OPT_JOIN_LEN));
 			return;
 		    }
 		    bp += 2;
 		    seq = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " JOIN %u", seq));
-		    opts_len -= 8;
+		    opts_len -= PGM_OPT_JOIN_LEN;
 		    break;
 
 		case PGM_OPT_NAK_BO_IVL:
-		    if (opt_len != 12) {
-			ND_PRINT((ndo, "[Bad OPT_NAK_BO_IVL option, length %u != 12]", opt_len));
+#define PGM_OPT_NAK_BO_IVL_LEN	(2+2+4+4)
+		    if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_NAK_BO_IVL option, length %u != %u]",
+			    opt_len, PGM_OPT_NAK_BO_IVL_LEN));
 			return;
 		    }
 		    bp += 2;
 		    offset = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    seq = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " BACKOFF ivl %u ivlseq %u", offset, seq));
-		    opts_len -= 12;
+		    opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
 		    break;
 
 		case PGM_OPT_NAK_BO_RNG:
-		    if (opt_len != 12) {
-			ND_PRINT((ndo, "[Bad OPT_NAK_BO_RNG option, length %u != 12]", opt_len));
+#define PGM_OPT_NAK_BO_RNG_LEN	(2+2+4+4)
+		    if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_NAK_BO_RNG option, length %u != %u]",
+			    opt_len, PGM_OPT_NAK_BO_RNG_LEN));
 			return;
 		    }
 		    bp += 2;
 		    offset = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    seq = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " BACKOFF max %u min %u", offset, seq));
-		    opts_len -= 12;
+		    opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
 		    break;
 
 		case PGM_OPT_REDIRECT:
+#define PGM_OPT_REDIRECT_FIXED_LEN	(2+2+2+2)
+		    if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u < %u]",
+			    opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
+			return;
+		    }
 		    bp += 2;
 		    nla_afnum = EXTRACT_16BITS(bp);
-		    bp += (2 * sizeof(uint16_t));
+		    bp += 2+2;
 		    switch (nla_afnum) {
 		    case AFNUM_INET:
-			if (opt_len != 4 + sizeof(struct in_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len));
+			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
+			        opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in_addr));
 			addrtostr(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in_addr);
-			opts_len -= 4 + sizeof(struct in_addr);
+			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr);
 			break;
 		    case AFNUM_INET6:
-			if (opt_len != 4 + sizeof(struct in6_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len));
+			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
+			        PGM_OPT_REDIRECT_FIXED_LEN, opt_len));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in6_addr));
 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in6_addr);
-			opts_len -= 4 + sizeof(struct in6_addr);
+			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr);
 			break;
 		    default:
 			goto trunc;
 			break;
 		    }
 
 		    ND_PRINT((ndo, " REDIRECT %s",  nla_buf));
 		    break;
 
 		case PGM_OPT_PARITY_PRM:
-		    if (opt_len != 8) {
-			ND_PRINT((ndo, "[Bad OPT_PARITY_PRM option, length %u != 8]", opt_len));
+#define PGM_OPT_PARITY_PRM_LEN	(2+2+4)
+		    if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_PARITY_PRM option, length %u != %u]",
+			    opt_len, PGM_OPT_PARITY_PRM_LEN));
 			return;
 		    }
 		    bp += 2;
 		    len = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " PARITY MAXTGS %u", len));
-		    opts_len -= 8;
+		    opts_len -= PGM_OPT_PARITY_PRM_LEN;
 		    break;
 
 		case PGM_OPT_PARITY_GRP:
-		    if (opt_len != 8) {
-			ND_PRINT((ndo, "[Bad OPT_PARITY_GRP option, length %u != 8]", opt_len));
+#define PGM_OPT_PARITY_GRP_LEN	(2+2+4)
+		    if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_PARITY_GRP option, length %u != %u]",
+			    opt_len, PGM_OPT_PARITY_GRP_LEN));
 			return;
 		    }
 		    bp += 2;
 		    seq = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " PARITY GROUP %u", seq));
-		    opts_len -= 8;
+		    opts_len -= PGM_OPT_PARITY_GRP_LEN;
 		    break;
 
 		case PGM_OPT_CURR_TGSIZE:
-		    if (opt_len != 8) {
-			ND_PRINT((ndo, "[Bad OPT_CURR_TGSIZE option, length %u != 8]", opt_len));
+#define PGM_OPT_CURR_TGSIZE_LEN	(2+2+4)
+		    if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_CURR_TGSIZE option, length %u != %u]",
+			    opt_len, PGM_OPT_CURR_TGSIZE_LEN));
 			return;
 		    }
 		    bp += 2;
 		    len = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    ND_PRINT((ndo, " PARITY ATGS %u", len));
-		    opts_len -= 8;
+		    opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
 		    break;
 
 		case PGM_OPT_NBR_UNREACH:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_NBR_UNREACH option, length %u != 4]", opt_len));
+#define PGM_OPT_NBR_UNREACH_LEN	(2+2)
+		    if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_NBR_UNREACH option, length %u != %u]",
+			    opt_len, PGM_OPT_NBR_UNREACH_LEN));
 			return;
 		    }
 		    bp += 2;
 		    ND_PRINT((ndo, " NBR_UNREACH"));
-		    opts_len -= 4;
+		    opts_len -= PGM_OPT_NBR_UNREACH_LEN;
 		    break;
 
 		case PGM_OPT_PATH_NLA:
 		    ND_PRINT((ndo, " PATH_NLA [%d]", opt_len));
 		    bp += opt_len;
 		    opts_len -= opt_len;
 		    break;
 
 		case PGM_OPT_SYN:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_SYN option, length %u != 4]", opt_len));
+#define PGM_OPT_SYN_LEN	(2+2)
+		    if (opt_len != PGM_OPT_SYN_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_SYN option, length %u != %u]",
+			    opt_len, PGM_OPT_SYN_LEN));
 			return;
 		    }
 		    bp += 2;
 		    ND_PRINT((ndo, " SYN"));
-		    opts_len -= 4;
+		    opts_len -= PGM_OPT_SYN_LEN;
 		    break;
 
 		case PGM_OPT_FIN:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_FIN option, length %u != 4]", opt_len));
+#define PGM_OPT_FIN_LEN	(2+2)
+		    if (opt_len != PGM_OPT_FIN_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_FIN option, length %u != %u]",
+			    opt_len, PGM_OPT_FIN_LEN));
 			return;
 		    }
 		    bp += 2;
 		    ND_PRINT((ndo, " FIN"));
-		    opts_len -= 4;
+		    opts_len -= PGM_OPT_FIN_LEN;
 		    break;
 
 		case PGM_OPT_RST:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_RST option, length %u != 4]", opt_len));
+#define PGM_OPT_RST_LEN	(2+2)
+		    if (opt_len != PGM_OPT_RST_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_RST option, length %u != %u]",
+			    opt_len, PGM_OPT_RST_LEN));
 			return;
 		    }
 		    bp += 2;
 		    ND_PRINT((ndo, " RST"));
-		    opts_len -= 4;
+		    opts_len -= PGM_OPT_RST_LEN;
 		    break;
 
 		case PGM_OPT_CR:
 		    ND_PRINT((ndo, " CR"));
 		    bp += opt_len;
 		    opts_len -= opt_len;
 		    break;
 
 		case PGM_OPT_CRQST:
-		    if (opt_len != 4) {
-			ND_PRINT((ndo, "[Bad OPT_CRQST option, length %u != 4]", opt_len));
+#define PGM_OPT_CRQST_LEN	(2+2)
+		    if (opt_len != PGM_OPT_CRQST_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_CRQST option, length %u != %u]",
+			    opt_len, PGM_OPT_CRQST_LEN));
 			return;
 		    }
 		    bp += 2;
 		    ND_PRINT((ndo, " CRQST"));
-		    opts_len -= 4;
+		    opts_len -= PGM_OPT_CRQST_LEN;
 		    break;
 
 		case PGM_OPT_PGMCC_DATA:
+#define PGM_OPT_PGMCC_DATA_FIXED_LEN	(2+2+4+2+2)
+		    if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
+			ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u < %u]",
+			    opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
+			return;
+		    }
 		    bp += 2;
 		    offset = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    nla_afnum = EXTRACT_16BITS(bp);
-		    bp += (2 * sizeof(uint16_t));
+		    bp += 2+2;
 		    switch (nla_afnum) {
 		    case AFNUM_INET:
-			if (opt_len != 12 + sizeof(struct in_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
+			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
+			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in_addr));
 			addrtostr(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in_addr);
-			opts_len -= 12 + sizeof(struct in_addr);
+			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr);
 			break;
 		    case AFNUM_INET6:
-			if (opt_len != 12 + sizeof(struct in6_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
+			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
+			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in6_addr));
 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in6_addr);
-			opts_len -= 12 + sizeof(struct in6_addr);
+			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr);
 			break;
 		    default:
 			goto trunc;
 			break;
 		    }
 
 		    ND_PRINT((ndo, " PGMCC DATA %u %s", offset, nla_buf));
 		    break;
 
 		case PGM_OPT_PGMCC_FEEDBACK:
+#define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN	(2+2+4+2+2)
+		    if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
+			ND_PRINT((ndo, "[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
+			    opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
+			return;
+		    }
 		    bp += 2;
 		    offset = EXTRACT_32BITS(bp);
-		    bp += sizeof(uint32_t);
+		    bp += 4;
 		    nla_afnum = EXTRACT_16BITS(bp);
-		    bp += (2 * sizeof(uint16_t));
+		    bp += 2+2;
 		    switch (nla_afnum) {
 		    case AFNUM_INET:
-			if (opt_len != 12 + sizeof(struct in_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
+			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
+			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in_addr));
 			addrtostr(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in_addr);
-			opts_len -= 12 + sizeof(struct in_addr);
+			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr);
 			break;
 		    case AFNUM_INET6:
-			if (opt_len != 12 + sizeof(struct in6_addr)) {
-			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
+			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr)) {
+			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
+			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
 			    return;
 			}
 			ND_TCHECK2(*bp, sizeof(struct in6_addr));
 			addrtostr6(bp, nla_buf, sizeof(nla_buf));
 			bp += sizeof(struct in6_addr);
-			opts_len -= 12 + sizeof(struct in6_addr);
+			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr);
 			break;
 		    default:
 			goto trunc;
 			break;
 		    }
diff --git a/tests/TESTLIST b/tests/TESTLIST
index 68617bf44a..9423723288 100644
--- a/tests/TESTLIST
+++ b/tests/TESTLIST
@@ -518,10 +518,11 @@ esis_snpa_asan-2	esis_snpa_asan-2.pcap		esis_snpa_asan-2.out	-v
 esis_snpa_asan-3	esis_snpa_asan-3.pcap		esis_snpa_asan-3.out	-v
 esis_snpa_asan-4	esis_snpa_asan-4.pcap		esis_snpa_asan-4.out	-v
 esis_snpa_asan-5	esis_snpa_asan-5.pcap		esis_snpa_asan-5.out	-v
 dhcp6_reconf_asan	dhcp6_reconf_asan.pcap		dhcp6_reconf_asan.out	-v
 pgm_opts_asan		pgm_opts_asan.pcap		pgm_opts_asan.out	-v
+pgm_opts_asan_2		pgm_opts_asan_2.pcap		pgm_opts_asan_2.out	-v
 
 # RTP tests
 # fuzzed pcap
 rtp-seg-fault-1  rtp-seg-fault-1.pcap  rtp-seg-fault-1.out  -v -T rtp
 rtp-seg-fault-2  rtp-seg-fault-2.pcap  rtp-seg-fault-2.out  -v -T rtp
diff --git a/tests/pgm_opts_asan_2.out b/tests/pgm_opts_asan_2.out
new file mode 100644
index 0000000000..7e948d4123
--- /dev/null
+++ b/tests/pgm_opts_asan_2.out
@@ -0,0 +1,2 @@
+IP (tos 0x41,ECT(1), id 0, offset 0, flags [none], proto PGM (113), length 32639, options (unknown 89 [bad length 232]), bad cksum 5959 (->96b9)!)
+    128.121.89.107 > 89.89.16.63: 128.121.89.107.4 > 89.89.16.63.225: PGM, length 0 0x3414eb1f0022 UNKNOWN type 0x1f OPTS LEN 225 OPT_1F [13]  OPT_06 [26] [Bad OPT_PGMCC_DATA option, length 4 < 12]
diff --git a/tests/pgm_opts_asan_2.pcap b/tests/pgm_opts_asan_2.pcap
new file mode 100644
index 0000000000..28773fd9e8
Binary files /dev/null and b/tests/pgm_opts_asan_2.pcap differ

Vulnerable code: 		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != 4]", opt_len));
		    bp += sizeof(uint16_t);
		    opts_len -= 4;
		    if (opt_len != 16) {
			ND_PRINT((ndo, "[Bad OPT_FRAGMENT option, length %u != 16]", opt_len));
		    bp += sizeof(uint32_t);
		    bp += sizeof(uint32_t);
		    bp += sizeof(uint32_t);
		    opts_len -= 16;
		    opt_len -= sizeof(uint32_t);	/* option header */
			if (opt_len < sizeof(uint32_t)) {
			ND_TCHECK2(*bp, sizeof(uint32_t));
			bp += sizeof(uint32_t);
			opt_len -= sizeof(uint32_t);
			opts_len -= sizeof(uint32_t);
		    if (opt_len != 8) {
			ND_PRINT((ndo, "[Bad OPT_JOIN option, length %u != 8]", opt_len));
		    bp += sizeof(uint32_t);
		    opts_len -= 8;
		    if (opt_len != 12) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_IVL option, length %u != 12]", opt_len));
		    bp += sizeof(uint32_t);
		    bp += sizeof(uint32_t);
		    opts_len -= 12;
		    if (opt_len != 12) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_RNG option, length %u != 12]", opt_len));
		    bp += sizeof(uint32_t);
		    bp += sizeof(uint32_t);
		    opts_len -= 12;
		    bp += (2 * sizeof(uint16_t));
			if (opt_len != 4 + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len));
			opts_len -= 4 + sizeof(struct in_addr);
			if (opt_len != 4 + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len));
			opts_len -= 4 + sizeof(struct in6_addr);
		    if (opt_len != 8) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_PRM option, length %u != 8]", opt_len));
		    bp += sizeof(uint32_t);
		    opts_len -= 8;
		    if (opt_len != 8) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_GRP option, length %u != 8]", opt_len));
		    bp += sizeof(uint32_t);
		    opts_len -= 8;
		    if (opt_len != 8) {
			ND_PRINT((ndo, "[Bad OPT_CURR_TGSIZE option, length %u != 8]", opt_len));
		    bp += sizeof(uint32_t);
		    opts_len -= 8;
		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_NBR_UNREACH option, length %u != 4]", opt_len));
		    opts_len -= 4;
		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_SYN option, length %u != 4]", opt_len));
		    opts_len -= 4;
		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_FIN option, length %u != 4]", opt_len));
		    opts_len -= 4;
		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_RST option, length %u != 4]", opt_len));
		    opts_len -= 4;
		    if (opt_len != 4) {
			ND_PRINT((ndo, "[Bad OPT_CRQST option, length %u != 4]", opt_len));
		    opts_len -= 4;
		    bp += sizeof(uint32_t);
		    bp += (2 * sizeof(uint16_t));
			if (opt_len != 12 + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
			opts_len -= 12 + sizeof(struct in_addr);
			if (opt_len != 12 + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
			opts_len -= 12 + sizeof(struct in6_addr);
		    bp += sizeof(uint32_t);
		    bp += (2 * sizeof(uint16_t));
			if (opt_len != 12 + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
			opts_len -= 12 + sizeof(struct in_addr);
			if (opt_len != 12 + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len));
			opts_len -= 12 + sizeof(struct in6_addr);
Fixed code: #define PGM_OPT_LENGTH_LEN	(2+2)
		    if (opt_len != PGM_OPT_LENGTH_LEN) {
			ND_PRINT((ndo, "[Bad OPT_LENGTH option, length %u != %u]",
			    opt_len, PGM_OPT_LENGTH_LEN));
		    bp += 2;
		    opts_len -= PGM_OPT_LENGTH_LEN;
#define PGM_OPT_FRAGMENT_LEN	(2+2+4+4+4)
		    if (opt_len != PGM_OPT_FRAGMENT_LEN) {
			ND_PRINT((ndo, "[Bad OPT_FRAGMENT option, length %u != %u]",
			    opt_len, PGM_OPT_FRAGMENT_LEN));
		    bp += 4;
		    bp += 4;
		    bp += 4;
		    opts_len -= PGM_OPT_FRAGMENT_LEN;
		    opt_len -= 4;	/* option header */
			if (opt_len < 4) {
			ND_TCHECK2(*bp, 4);
			bp += 4;
			opt_len -= 4;
			opts_len -= 4;
#define PGM_OPT_JOIN_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_JOIN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_JOIN option, length %u != %u]",
			    opt_len, PGM_OPT_JOIN_LEN));
		    bp += 4;
		    opts_len -= PGM_OPT_JOIN_LEN;
#define PGM_OPT_NAK_BO_IVL_LEN	(2+2+4+4)
		    if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_IVL option, length %u != %u]",
			    opt_len, PGM_OPT_NAK_BO_IVL_LEN));
		    bp += 4;
		    bp += 4;
		    opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
#define PGM_OPT_NAK_BO_RNG_LEN	(2+2+4+4)
		    if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NAK_BO_RNG option, length %u != %u]",
			    opt_len, PGM_OPT_NAK_BO_RNG_LEN));
		    bp += 4;
		    bp += 4;
		    opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
#define PGM_OPT_REDIRECT_FIXED_LEN	(2+2+2+2)
		    if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u < %u]",
			    opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
			return;
		    }
		    bp += 2+2;
			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
			        opt_len, PGM_OPT_REDIRECT_FIXED_LEN));
			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in_addr);
			if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_REDIRECT option, length %u != %u + address size]",
			        PGM_OPT_REDIRECT_FIXED_LEN, opt_len));
			opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(struct in6_addr);
#define PGM_OPT_PARITY_PRM_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_PRM option, length %u != %u]",
			    opt_len, PGM_OPT_PARITY_PRM_LEN));
		    bp += 4;
		    opts_len -= PGM_OPT_PARITY_PRM_LEN;
#define PGM_OPT_PARITY_GRP_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PARITY_GRP option, length %u != %u]",
			    opt_len, PGM_OPT_PARITY_GRP_LEN));
		    bp += 4;
		    opts_len -= PGM_OPT_PARITY_GRP_LEN;
#define PGM_OPT_CURR_TGSIZE_LEN	(2+2+4)
		    if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
			ND_PRINT((ndo, "[Bad OPT_CURR_TGSIZE option, length %u != %u]",
			    opt_len, PGM_OPT_CURR_TGSIZE_LEN));
		    bp += 4;
		    opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
#define PGM_OPT_NBR_UNREACH_LEN	(2+2)
		    if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
			ND_PRINT((ndo, "[Bad OPT_NBR_UNREACH option, length %u != %u]",
			    opt_len, PGM_OPT_NBR_UNREACH_LEN));
		    opts_len -= PGM_OPT_NBR_UNREACH_LEN;
#define PGM_OPT_SYN_LEN	(2+2)
		    if (opt_len != PGM_OPT_SYN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_SYN option, length %u != %u]",
			    opt_len, PGM_OPT_SYN_LEN));
		    opts_len -= PGM_OPT_SYN_LEN;
#define PGM_OPT_FIN_LEN	(2+2)
		    if (opt_len != PGM_OPT_FIN_LEN) {
			ND_PRINT((ndo, "[Bad OPT_FIN option, length %u != %u]",
			    opt_len, PGM_OPT_FIN_LEN));
		    opts_len -= PGM_OPT_FIN_LEN;
#define PGM_OPT_RST_LEN	(2+2)
		    if (opt_len != PGM_OPT_RST_LEN) {
			ND_PRINT((ndo, "[Bad OPT_RST option, length %u != %u]",
			    opt_len, PGM_OPT_RST_LEN));
		    opts_len -= PGM_OPT_RST_LEN;
#define PGM_OPT_CRQST_LEN	(2+2)
		    if (opt_len != PGM_OPT_CRQST_LEN) {
			ND_PRINT((ndo, "[Bad OPT_CRQST option, length %u != %u]",
			    opt_len, PGM_OPT_CRQST_LEN));
		    opts_len -= PGM_OPT_CRQST_LEN;
#define PGM_OPT_PGMCC_DATA_FIXED_LEN	(2+2+4+2+2)
		    if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u < %u]",
			    opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			return;
		    }
		    bp += 4;
		    bp += 2+2;
			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in_addr);
			if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN));
			opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(struct in6_addr);
#define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN	(2+2+4+2+2)
		    if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
			ND_PRINT((ndo, "[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
			    opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			return;
		    }
		    bp += 4;
		    bp += 2+2;
			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in_addr);
			if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr)) {
			    ND_PRINT((ndo, "[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
			        opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN));
			opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(struct in6_addr);
pgm_opts_asan_2		pgm_opts_asan_2.pcap		pgm_opts_asan_2.out	-v
IP (tos 0x41,ECT(1), id 0, offset 0, flags [none], proto PGM (113), length 32639, options (unknown 89 [bad length 232]), bad cksum 5959 (->96b9)!)
    128.121.89.107 > 89.89.16.63: 128.121.89.107.4 > 89.89.16.63.225: PGM, length 0 0x3414eb1f0022 UNKNOWN type 0x1f OPTS LEN 225 OPT_1F [13]  OPT_06 [26] [Bad OPT_PGMCC_DATA option, length 4 < 12]