Bug 15175

Summary: tcp_input() fails to update m->m_pkthdr.len
Product: Base System Reporter: Archie Cobbs <archie>
Component: kernAssignee: freebsd-bugs (Nobody) <bugs>
Status: Closed FIXED    
Severity: Affects Only Me    
Priority: Normal    
Version: 3.3-STABLE   
Hardware: Any   
OS: Any   

Description Archie Cobbs 1999-11-30 01:50:01 UTC
See sys/netinet/tcp_input.c, line 376:

        /*
         * Drop TCP, IP headers and TCP options.
         */
        m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
        m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);

Notice that m->m_pkthdr.len is not updated, and therefore the mbuf
becomes inconsistent.  Apparently this doesn't matter much in normal
use.  However, netgraph(4) is strict about checking the consistency
of mbufs and the above omission causes a panic later on.

Fix: 

if ((m->m_flags & M_PKTHDR) != 0)
	m->m_pkthdr.len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);

   [ Can we always assume ((m->m_flags & M_PKTHDR) != 0) here?? ]
How-To-Repeat: 
	Input TCP data
Comment 1 Archie Cobbs 2000-01-27 00:32:47 UTC
This bug is fixed in 4.0-current thanks to the IPv6 integration.
However, it remains in 3.4-stable.. below is a patch that fixes it
with some KASSERT's to insure that the M_PKTHDR assumptions are
correct.

-Archie

___________________________________________________________________________
Archie Cobbs   *   Whistle Communications, Inc.  *   http://www.whistle.com

Index: sys/netinet/ip_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.111.2.5
diff -u -r1.111.2.5 ip_input.c
--- ip_input.c	2000/01/18 16:03:55	1.111.2.5
+++ ip_input.c	2000/01/26 23:57:18
@@ -909,9 +909,14 @@
 	(void) m_free(dtom(fp));
 	m->m_len += (IP_VHL_HL(ip->ip_vhl) << 2);
 	m->m_data -= (IP_VHL_HL(ip->ip_vhl) << 2);
-	/* some debugging cruft by sklower, below, will go away soon */
-	if (m->m_flags & M_PKTHDR) { /* XXX this should be done elsewhere */
+
+	/*
+	 * Recompute total packet length
+	 */
+	KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__));
+	{
 		register int plen = 0;
+
 		for (t = m; m; m = m->m_next)
 			plen += m->m_len;
 		t->m_pkthdr.len = plen;
Index: sys/netinet/tcp_input.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/tcp_input.c,v
retrieving revision 1.82.2.3
diff -u -r1.82.2.3 tcp_input.c
--- tcp_input.c	1999/10/14 11:49:38	1.82.2.3
+++ tcp_input.c	2000/01/26 23:57:22
@@ -292,6 +292,8 @@
 	short ostate = 0;
 #endif
 
+	KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__));
+
 	bzero((char *)&to, sizeof(to));
 
 	tcpstat.tcps_rcvtotal++;
@@ -371,8 +373,14 @@
 	/*
 	 * Drop TCP, IP headers and TCP options.
 	 */
-	m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
-	m->m_len  -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+	{
+		const int diff = sizeof(struct tcpiphdr) + off
+					- sizeof(struct tcphdr);
+
+		m->m_data += diff;
+		m->m_len -= diff;
+		m->m_pkthdr.len -= diff;
+	}
 
 	/*
 	 * Locate pcb for segment.
@@ -1954,8 +1962,11 @@
 	struct tcpiphdr *ti;
 	register struct mbuf *m;
 {
+	struct mbuf *m0 = m;
 	int cnt = ti->ti_urp - 1;
 
+	KASSERT(m->m_flags & M_PKTHDR, ("%s: not pkthdr", __FUNCTION__));
+
 	while (cnt >= 0) {
 		if (m->m_len > cnt) {
 			char *cp = mtod(m, caddr_t) + cnt;
@@ -1965,6 +1976,7 @@
 			tp->t_oobflags |= TCPOOB_HAVEDATA;
 			bcopy(cp+1, cp, (unsigned)(m->m_len - cnt - 1));
 			m->m_len--;
+			m0->m_pkthdr.len--;
 			return;
 		}
 		cnt -= m->m_len;
Comment 2 dd freebsd_committer freebsd_triage 2001-06-10 01:00:48 UTC
State Changed
From-To: open->closed

RELENG_3 no longer supported for anything but the most critical fixes.