kern/176144: Bug in m_split() when splitting M_EXT mbufs

Jacques Fourie jacques.fourie at gmail.com
Thu Feb 14 13:00:00 UTC 2013


>Number:         176144
>Category:       kern
>Synopsis:       Bug in m_split() when splitting M_EXT mbufs
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Feb 14 13:00:00 UTC 2013
>Closed-Date:
>Last-Modified:
>Originator:     Jacques Fourie
>Release:        10-current
>Organization:
Netronome Systems
>Environment:
FreeBSD fbsd10vm 10.0-CURRENT FreeBSD 10.0-CURRENT #7 r+45b4c99-dirty: Mon Jan  7 09:40:08 SAST 2013     root at fbsd10vm:/usr/obj/usr/home/jfourie/freebsd.git/sys/FBSD10VM  amd64
>Description:
The following text is copied from my original mail to -hackers:

Could someone please verify if m_split as in svn rev 245286 is doing the
right thing in the scenario where a mbuf chain is split with len0 falling
on a mbuf boundary and the mbuf in question being a M_EXT mbuf? Consider
the following example where m0 is a mbuf chain consisting of 2 M_EXT mbufs,
both 1448 bytes in length. Let len0 be 1448. The 'len0 > m->m_len' check
will be false so the for loop will not be entered in this case. We now have
len = 1448 and remain = 0 and m still points to the first mbuf in the
chain. Also assume that m0 is a pkthdr mbuf. A new pkthdr mbuf n will be
allocated and initialized before the following piece of code is executed :

extpacket:
         if (m->m_flags & M_EXT) {
                n->m_data = m->m_data + len;
                mb_dupcl(n, m);
         } else {
                bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain);
         } 
         n->m_len = remain;
         m->m_len = len;
         n->m_next = m->m_next;
         m->m_next = NULL;

As m is a M_EXT mbuf the code in the if() clause will be executed. The
problem is that m still points to the first mbuf so effectively the data
pointer for n is assigned to the end of m's data pointer. It should
actually point to the start of the data pointer of the next mbuf in the
original m0 chain, right?
>How-To-Repeat:

>Fix:
Attached patch fixes the issue for me

Patch attached with submission follows:

diff --git a/sys/kern/uipc_mbuf.c b/sys/kern/uipc_mbuf.c
index ab6163d..5c397fa 100644
--- a/sys/kern/uipc_mbuf.c
+++ b/sys/kern/uipc_mbuf.c
@@ -1132,6 +1132,23 @@ m_split(struct mbuf *m0, int len0, int wait)
 		return (NULL);
 	remain = m->m_len - len;
 	if (m0->m_flags & M_PKTHDR) {
+		if (remain == 0) {
+			if (m->m_next == NULL)
+				return (NULL);
+			if (!(m->m_next->m_flags & M_PKTHDR)) {
+				MGETHDR(n, wait, m0->m_type);
+				if (n == NULL)
+					return (NULL);
+				MH_ALIGN(n, 0);
+				n->m_next = m->m_next;
+			} else 
+				n = m->m_next;
+			m->m_next = NULL;
+			n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif;
+			n->m_pkthdr.len = m0->m_pkthdr.len - len0;
+			m0->m_pkthdr.len = len0;
+			return (n);
+		}
 		MGETHDR(n, wait, m0->m_type);
 		if (n == NULL)
 			return (NULL);


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list