Keep mbuf data alignment intact in m_defrag()
authorclaudio <claudio@openbsd.org>
Wed, 21 Feb 2024 09:28:29 +0000 (09:28 +0000)
committerclaudio <claudio@openbsd.org>
Wed, 21 Feb 2024 09:28:29 +0000 (09:28 +0000)
The recent TSO support in em(4) triggered an alignment error on the TCP
header. In em(4) m_defrag() is called before setting up the TSO dma bits
and with that the TCP header was suddenly no longer aligned. Like other
mbuf functions preserve the data alignment in m_defrag() to prevent such
unaligned packets.

With help and OK bluhm@ mglocker@

sys/kern/uipc_mbuf.c

index 5d1f9a7..ec675a2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: uipc_mbuf.c,v 1.288 2023/10/20 16:25:15 bluhm Exp $   */
+/*     $OpenBSD: uipc_mbuf.c,v 1.289 2024/02/21 09:28:29 claudio Exp $ */
 /*     $NetBSD: uipc_mbuf.c,v 1.15.4.1 1996/06/13 17:11:44 cgd Exp $   */
 
 /*
@@ -550,21 +550,24 @@ int
 m_defrag(struct mbuf *m, int how)
 {
        struct mbuf *m0;
+       unsigned int adj;
 
        if (m->m_next == NULL)
                return (0);
 
        KASSERT(m->m_flags & M_PKTHDR);
 
+       adj = mtod(m, unsigned long) & (sizeof(long) - 1);
        if ((m0 = m_gethdr(how, m->m_type)) == NULL)
                return (ENOBUFS);
-       if (m->m_pkthdr.len > MHLEN) {
-               MCLGETL(m0, how, m->m_pkthdr.len);
+       if (m->m_pkthdr.len + adj > MHLEN) {
+               MCLGETL(m0, how, m->m_pkthdr.len + adj);
                if (!(m0->m_flags & M_EXT)) {
                        m_free(m0);
                        return (ENOBUFS);
                }
        }
+       m0->m_data += adj;
        m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
        m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
 
@@ -583,9 +586,9 @@ m_defrag(struct mbuf *m, int how)
                memcpy(&m->m_ext, &m0->m_ext, sizeof(struct mbuf_ext));
                MCLINITREFERENCE(m);
                m->m_flags |= m0->m_flags & (M_EXT|M_EXTWR);
-               m->m_data = m->m_ext.ext_buf;
+               m->m_data = m->m_ext.ext_buf + adj;
        } else {
-               m->m_data = m->m_pktdat;
+               m->m_data = m->m_pktdat + adj;
                memcpy(m->m_data, m0->m_data, m0->m_len);
        }
        m->m_pkthdr.len = m->m_len = m0->m_len;