git: 73cceb046cd8 - main - iflib: Fix several memory handling issues around iflib_encap()
Andrew Gallatin <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.cvs.src |
|---|---|
| Message-ID | <6a7f7036.37a9d.a16905f__7562.4387872935$1786736723$gmane$org@gitrepo.freebsd.org> |
The branch main has been updated by gallatin: URL: https://cgit.FreeBSD.org/src/commit/?id=73cceb046cd86187bc3e3bad07dd2422ee102552 commit 73cceb046cd86187bc3e3bad07dd2422ee102552 Author: Andrew Gallatin <[email protected]> AuthorDate: 2026-08-14 19:38:34 +0000 Commit: Andrew Gallatin <[email protected]> CommitDate: 2026-08-14 19:42:50 +0000 iflib: Fix several memory handling issues around iflib_encap() - Fixed memory leaks around m_dup() not freeing the original chain on failure. If we return ENOMEM, we are expected to have freed the chain, else the mbuf would be leaked. Also updated iflib_ether_pad() to follow the same structure. - In iflib_parse_header() o Fixed a bug where the ip/ip6 and th pointers may point into a freed chain after m_pullup. Those pointers must be reset to point into the new chain. o Eliminate ENXIO returns for non-TCP TSO sends (which would violate the mbuf ownership contract if they could happen). Since they cannot happen, I made them assertions instead. - in iflib_ether_pad(), return ENOMEM after freeing mbuf, so that mp_ring knows it is free. An ENOBUFS error will cause the mp_ring path to retain the mbuf and retry - in iflib_encap(): o Fix a leak when bus_dmamap_load_mbuf_sg() returns ENOMEM o Fix a use-after-free in the mp_ring path when a driver using ktls frees an mbuf and returns ENOBUFS via iflib_encap() After this change the expection from iflib_encap is that: mp_ring: ENOBUFS can be returned only when we run out of descriptors (ENOBUFS causes mp_ring to retain the mbuf). simple_tx: iflib_encap() always consumes the mbuf, regardless of the return Note that iflib_debugnet_transmit(), like simple_tx, expects that iflib_encap() always consumes mbufs. This will be true after mp_ring is removed, and its such a rare special case (overrunning the ring during panic dumps) that I don't think its worth fixing in the meantime. Sponsored by: Netflix Reviewed by: kbowling, sumit.saxena_broadcom.com Differential Revision: https://reviews.freebsd.org/D58843 Fixes: 074ff8746388 --- sys/net/iflib.c | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/sys/net/iflib.c b/sys/net/iflib.c index abc56f159547..d2ac8d884a60 100644 --- a/sys/net/iflib.c +++ b/sys/net/iflib.c @@ -3262,13 +3262,12 @@ iflib_parse_header_partial(if_pkt_info_t pi, struct mbuf **mp, uint64_t *pullups *pullups = 0; m = *mp; if (!M_WRITABLE(m)) { - if ((m = m_dup(m, M_NOWAIT)) == NULL) { + m = m_dup(m, M_NOWAIT); + m_freem(*mp); + DBG_COUNTER_INC(tx_frees); + *mp = m; + if (m == NULL) return (ENOMEM); - } else { - m_freem(*mp); - DBG_COUNTER_INC(tx_frees); - *mp = m; - } } /* Fills out pi->ipi_etype */ @@ -3364,13 +3363,12 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) m = *mp; if ((sctx->isc_flags & IFLIB_NEED_SCRATCH) && M_WRITABLE(m) == 0) { - if ((m = m_dup(m, M_NOWAIT)) == NULL) { + m = m_dup(m, M_NOWAIT); + m_freem(*mp); + DBG_COUNTER_INC(tx_frees); + *mp = m; + if (m == NULL) return (ENOMEM); - } else { - m_freem(*mp); - DBG_COUNTER_INC(tx_frees); - *mp = m; - } } /* Fills out pi->ipi_etype */ @@ -3405,6 +3403,9 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) txq->ift_pullups++; if ((m = m_pullup(m, hlen)) == NULL) return (ENOMEM); + /* reset pointers after pullup */ + ip = (struct ip *)(m->m_data + pi->ipi_ehdrlen); + th = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2)); } pi->ipi_ip_hlen = ip->ip_hl << 2; pi->ipi_ipproto = ip->ip_p; @@ -3419,8 +3420,7 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) pi->ipi_tcp_seq = th->th_seq; } if (IS_TSO4(pi)) { - if (__predict_false(ip->ip_p != IPPROTO_TCP)) - return (ENXIO); + MPASS(ip->ip_p == IPPROTO_TCP); /* * TSO always requires hardware checksum offload. */ @@ -3451,6 +3451,8 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) txq->ift_pullups++; if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr))) == NULL)) return (ENOMEM); + /* reset pointers after pullup */ + ip6 = (struct ip6_hdr *)(m->m_data + pi->ipi_ehdrlen); } th = (struct tcphdr *)((caddr_t)ip6 + pi->ipi_ip_hlen); @@ -3466,14 +3468,16 @@ iflib_parse_header(iflib_txq_t txq, if_pkt_info_t pi, struct mbuf **mp) txq->ift_pullups++; if (__predict_false((m = m_pullup(m, pi->ipi_ehdrlen + sizeof(struct ip6_hdr) + sizeof(struct tcphdr))) == NULL)) return (ENOMEM); + /* reset pointers after pullup */ + ip6 = (struct ip6_hdr *)(m->m_data + pi->ipi_ehdrlen); + th = (struct tcphdr *)((caddr_t)ip6 + pi->ipi_ip_hlen); } pi->ipi_tcp_hflags = tcp_get_flags(th); pi->ipi_tcp_hlen = th->th_off << 2; pi->ipi_tcp_seq = th->th_seq; } if (IS_TSO6(pi)) { - if (__predict_false(ip6->ip6_nxt != IPPROTO_TCP)) - return (ENXIO); + MPASS(ip6->ip6_nxt == IPPROTO_TCP); /* * TSO always requires hardware checksum offload. */ @@ -3539,15 +3543,14 @@ iflib_ether_pad(device_t dev, struct mbuf **m_head, uint16_t min_frame_size) if (!M_WRITABLE(*m_head)) { new_head = m_dup(*m_head, M_NOWAIT); + m_freem(*m_head); + *m_head = new_head; if (new_head == NULL) { - m_freem(*m_head); device_printf(dev, "cannot pad short frame, m_dup() failed"); DBG_COUNTER_INC(encap_pad_mbuf_fail); DBG_COUNTER_INC(tx_frees); return (ENOMEM); } - m_freem(*m_head); - *m_head = new_head; } for (n = min_frame_size - (*m_head)->m_pkthdr.len; @@ -3557,10 +3560,11 @@ iflib_ether_pad(device_t dev, struct mbuf **m_head, uint16_t min_frame_size) if (n > 0) { m_freem(*m_head); + *m_head = NULL; device_printf(dev, "cannot pad short frame\n"); DBG_COUNTER_INC(encap_pad_mbuf_fail); DBG_COUNTER_INC(tx_frees); - return (ENOBUFS); + return (ENOMEM); } return (0); @@ -3665,8 +3669,7 @@ defrag: goto retry; break; case ENOMEM: - txq->ift_no_tx_dma_setup++; - break; + /* FALLTHROUGH */ default: txq->ift_no_tx_dma_setup++; m_freem(*m_headp); @@ -3776,6 +3779,9 @@ defrag: } goto defrag_failed; } + /* mp_ring assumes ENOBUFS means we didn't consume the mbuf */ + if (err == ENOBUFS && !ctx->ifc_sysctl_simple_tx) + err = ENOMEM; goto out_with_error; } /*