git: 7d508464f56c - main - carp: Fix pullup checks

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Sun, 01 Sep 2024 14:10:01 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=7d508464f56cf262465fd23ab96e357d8e42c927

commit 7d508464f56cf262465fd23ab96e357d8e42c927
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2024-08-31 01:18:23 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-09-01 14:09:53 +0000

    carp: Fix pullup checks
    
    The conditions used to test whether a pullup is needed were inverted.
    
    While here:
    - Fix a bogus assignment to "iplen": it's already initialized to *offp.
    - Use in_cksum_skip() instead of manually adjusting the data pointer.
      Otherwise the mbuf is temporarily in an invalid state, since m_len
      isn't updated to match.
    
    Reported by:    KMSAN
    Reviewed by:    kp
    Sponsored by:   Klara, Inc.
    Fixes:          37115154672f ("carp: support VRRPv3")
    Differential Revision:  https://reviews.freebsd.org/D46492
---
 sys/netinet/ip_carp.c | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/sys/netinet/ip_carp.c b/sys/netinet/ip_carp.c
index 9f163c1097ba..ab001d346313 100644
--- a/sys/netinet/ip_carp.c
+++ b/sys/netinet/ip_carp.c
@@ -516,7 +516,7 @@ static int
 carp_input(struct mbuf **mp, int *offp, int proto)
 {
 	struct mbuf *m = *mp;
-	struct ip *ip = mtod(m, struct ip *);
+	struct ip *ip;
 	struct vrrpv3_header *vh;
 	int iplen;
 	int minlen;
@@ -532,9 +532,6 @@ carp_input(struct mbuf **mp, int *offp, int proto)
 		return (IPPROTO_DONE);
 	}
 
-	iplen = ip->ip_hl << 2;
-	totlen = ntohs(ip->ip_len);
-
 	/* Ensure we have enough header to figure out the version. */
 	if (m->m_pkthdr.len < iplen + sizeof(*vh)) {
 		CARPSTATS_INC(carps_badlen);
@@ -545,14 +542,15 @@ carp_input(struct mbuf **mp, int *offp, int proto)
 		return (IPPROTO_DONE);
 	}
 
-	if (iplen + sizeof(*vh) < m->m_len) {
+	if (m->m_len < iplen + sizeof(*vh)) {
 		if ((m = m_pullup(m, iplen + sizeof(*vh))) == NULL) {
 			CARPSTATS_INC(carps_hdrops);
 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
 			return (IPPROTO_DONE);
 		}
-		ip = mtod(m, struct ip *);
 	}
+	ip = mtod(m, struct ip *);
+	totlen = ntohs(ip->ip_len);
 	vh = (struct vrrpv3_header *)((char *)ip + iplen);
 
 	switch (vh->vrrp_version) {
@@ -581,7 +579,7 @@ carp_input(struct mbuf **mp, int *offp, int proto)
 		return (IPPROTO_DONE);
 	}
 
-	if (iplen + minlen < m->m_len) {
+	if (m->m_len < iplen + minlen) {
 		if ((m = m_pullup(m, iplen + minlen)) == NULL) {
 			CARPSTATS_INC(carps_hdrops);
 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
@@ -596,15 +594,13 @@ carp_input(struct mbuf **mp, int *offp, int proto)
 		struct carp_header *ch;
 
 		/* verify the CARP checksum */
-		m->m_data += iplen;
-		if (in_cksum(m, totlen - iplen)) {
+		if (in_cksum_skip(m, totlen, iplen)) {
 			CARPSTATS_INC(carps_badsum);
 			CARP_DEBUG("%s: checksum failed on %s\n", __func__,
 			    if_name(m->m_pkthdr.rcvif));
 			m_freem(m);
 			break;
 		}
-		m->m_data -= iplen;
 		ch = (struct carp_header *)((char *)ip + iplen);
 		carp_input_c(m, ch, AF_INET, ip->ip_ttl);
 		break;
@@ -689,7 +685,7 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 		return (IPPROTO_DONE);
 	}
 
-	if (sizeof (*ip6) + minlen < m->m_len) {
+	if (m->m_len < sizeof(*ip6) + minlen) {
 		if ((m = m_pullup(m, sizeof(*ip6) + minlen)) == NULL) {
 			CARPSTATS_INC(carps_hdrops);
 			CARP_DEBUG("%s():%d: pullup failed\n", __func__, __LINE__);
@@ -704,15 +700,14 @@ carp6_input(struct mbuf **mp, int *offp, int proto)
 		struct carp_header *ch;
 
 		/* verify the CARP checksum */
-		m->m_data += *offp;
-		if (in_cksum(m, sizeof(struct carp_header))) {
+		if (in_cksum_skip(m, *offp + sizeof(struct carp_header),
+		    *offp)) {
 			CARPSTATS_INC(carps_badsum);
 			CARP_DEBUG("%s: checksum failed, on %s\n", __func__,
 			    if_name(m->m_pkthdr.rcvif));
 			m_freem(m);
 			break;
 		}
-		m->m_data -= *offp;
 		ch = (struct carp_header *)((char *)ip6 + sizeof(*ip6));
 		carp_input_c(m, ch, AF_INET6, ip6->ip6_hlim);
 		break;