⬢ DragonFlyBSD Kernel Audit
DF-3362 / fix.diff
← back to finding ↓ download raw
--- a/sys/net/pf/pf_norm.c	2026-09-11 10:08:39.254979306 +0000
+++ b/sys/net/pf/pf_norm.c	2026-09-11 10:09:05.382646092 +0000
@@ -659,11 +659,32 @@
 				 * I'll pull a rabbit out of my laptop.
 				 */
 				*m0 = m_dup(m, M_NOWAIT);
+				if (*m0 == NULL)
+					goto no_mem;
 				/* From KAME Project : We have missed this! */
 				m_adj(*m0, (h->ip_hl << 2) -
 				    (*m0)->m_pkthdr.len);
-				if (*m0 == NULL)
-					goto no_mem;
+				/* NOTE (DF-3362): m_adj(9) does not unlink
+				 * trailing mbufs whose length it zeroes while
+				 * tail-trimming, so the m_dup()ed chain can
+				 * keep zero-length mbufs linked after the
+				 * header trim.  m_cat() would append the
+				 * payload after those empties and the
+				 * single-mbuf invariant below would fire on
+				 * INVARIANTS kernels.  Unlink them.
+				 */
+				{
+					struct mbuf **pmp = &(*m0)->m_next;
+					while (*pmp != NULL) {
+						if ((*pmp)->m_len == 0) {
+							struct mbuf *zm = *pmp;
+							*pmp = zm->m_next;
+							m_free(zm);
+						} else {
+							pmp = &(*pmp)->m_next;
+						}
+					}
+				}
 				KASSERT(((*m0)->m_next == NULL),
 				    ("(*m0)->m_next != NULL: %s",
 				    __func__));
@@ -681,9 +702,13 @@
 
 				h = mtod(m, struct ip *);
 
-				KASSERT(((int)m->m_len ==
+				/* NOTE (DF-3362): the result is single-mbuf
+				 * only when the payload fits into the header
+				 * mbuf; assert on the recomputed total.
+				 */
+				KASSERT(((int)m->m_pkthdr.len ==
 				    ntohs(h->ip_len) - precut),
-				    ("m->m_len != h->ip_len - precut: %s",
+				    ("m_pkthdr.len != h->ip_len - precut: %s",
 				    __func__));
 				h->ip_off = htons(ntohs(h->ip_off) +
 						  (precut >> 3));