⬢ DragonFlyBSD Kernel Audit
DF-3422 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet/ip_gre.c
+++ b/sys/netinet/ip_gre.c
@@ -177,6 +177,18 @@
 		return(0);
 	}
 
+	/*
+	 * The GRE header and any optional fields (checksum/offset, key,
+	 * sequence) must actually be present in the packet.  hlen is
+	 * computed purely from the attacker-controlled flag bits and can
+	 * exceed the packet length, which would make m_len and
+	 * m_pkthdr.len go negative (DF-3422).
+	 */
+	if (m->m_pkthdr.len < hlen) {
+		m_freem(m);
+		return (1);
+	}
+
 	m->m_data += hlen;
 	m->m_len -= hlen;
 	m->m_pkthdr.len -= hlen;
@@ -189,7 +201,12 @@
 	}
 
 	m->m_pkthdr.rcvif = &sc->sc_if;
-	m->m_flags &= ~M_HASH;
+	/*
+	 * The outer packet's hash/length-check state does not apply to
+	 * the decapsulated inner packet; force ip_hashfn() to run
+	 * ip_lengthcheck() on it (DF-3422, cf. gif DF-3321).
+	 */
+	m->m_flags &= ~(M_HASH | M_LENCHECKED);
 	netisr_queue(isr, m);
 	return(1);	/* packet is done, no further processing needed */
 }
@@ -226,6 +243,18 @@
 	} else {
 		msiz = MOB_H_SIZ_S;
 	}
+
+	/*
+	 * The mobile header must be present in the packet: msiz is
+	 * selected by the S-bit of a header that a short datagram may
+	 * not carry, and the bcopy() length below would go negative and
+	 * underflow to a huge size_t (DF-3423).
+	 */
+	if (m->m_len < (ip->ip_hl << 2) + msiz) {
+		m_freem(m);
+		return(IPPROTO_DONE);
+	}
+
 	mip->mi.ip_dst.s_addr = mip->mh.odst;
 	mip->mi.ip_p = (ntohs(mip->mh.proto) >> 8);
 
@@ -244,7 +273,12 @@
 	 * rip_input already decreased ip_len byt the length of the
 	 * IP header.
 	 */
-	ip->ip_len = htons(ntohs(ip->ip_len) + sizeof(struct ip) - msiz);
+	/*
+	 * NOTE: ip_len is still the FULL outer datagram length
+	 * (DragonFly does not pre-decrement it), so only the mobile
+	 * header size is removed (DF-3423).
+	 */
+	ip->ip_len = htons(ntohs(ip->ip_len) - msiz);
 	
 	ip->ip_sum = 0;
 	ip->ip_sum = in_cksum(m, (ip->ip_hl << 2));
@@ -258,6 +292,10 @@
 
 	m->m_pkthdr.rcvif = &sc->sc_if;
 
+	/*
+	 * The decapsulated packet has not been characterized (DF-3423).
+	 */
+	m->m_flags &= ~(M_HASH | M_LENCHECKED);
 	netisr_queue(NETISR_IP, m);
 	return(IPPROTO_DONE);
 }