git: 8738545c54fe - stable/14 - tcp: Preserve borrowed ICMPv6 error mbufs
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 26 Oct 2026 19:46:59 UTC
The branch stable/14 has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=8738545c54fefc411ceb80242274fc94f9063051
commit 8738545c54fefc411ceb80242274fc94f9063051
Author: Andrew Griffiths <andrew@calif.io>
AuthorDate: 2026-10-05 13:14:38 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-26 14:12:15 +0000
tcp: Preserve borrowed ICMPv6 error mbufs
The TCP-over-UDP ICMP callback receives ip6c_m as a borrowed chain owned
by icmp6_notify_error. It used m_pulldown() to obtain a contiguous UDP
header even though that API frees the complete chain when the requested
range is unavailable. The callback could then return without
propagating the lost ownership, leaving raw ICMP delivery to read and
free a stale head.
Validate that the complete UDP header was quoted, copy it through the
chain with m_copydata(), and temporarily advance the parser offset
instead of consuming bytes from the caller-owned packet.
A remote sender can reach the old path when TCP UDP tunneling is enabled
by quoting the configured local UDP source port in an ICMPv6 error and
including only four through seven UDP-header bytes.
Signed-off-by: Andrew Griffiths <andrew@calif.io>
Reviewed by: pouria, tuexen, markj
MFC after: 1 week
Differential Revision: https://reviews.freebsd.org/D60143
(cherry picked from commit 80af0a32e48bff6ad97e768b16d2dbc01ac8d1c3)
---
sys/netinet/tcp_subr.c | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c
index 31f52cbebffa..3df756f23d49 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -3201,25 +3201,21 @@ static void
tcp6_ctlinput_viaudp(udp_tun_icmp_param_t param)
{
struct ip6ctlparam *ip6cp = param.ip6cp;
- struct mbuf *m;
- struct udphdr *udp;
+ struct udphdr udp;
uint16_t port;
- m = m_pulldown(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(struct udphdr), NULL);
- if (m == NULL) {
+ if (ip6cp->ip6c_m->m_pkthdr.len <
+ ip6cp->ip6c_off + sizeof(struct udphdr))
return;
- }
- udp = mtod(m, struct udphdr *);
- if (ntohs(udp->uh_sport) != V_tcp_udp_tunneling_port) {
+ m_copydata(ip6cp->ip6c_m, ip6cp->ip6c_off, sizeof(udp),
+ (caddr_t)&udp);
+ if (ntohs(udp.uh_sport) != V_tcp_udp_tunneling_port)
return;
- }
- port = udp->uh_dport;
- m_adj(m, sizeof(struct udphdr));
- if ((m->m_flags & M_PKTHDR) == 0) {
- ip6cp->ip6c_m->m_pkthdr.len -= sizeof(struct udphdr);
- }
+ port = udp.uh_dport;
+ ip6cp->ip6c_off += sizeof(struct udphdr);
/* Now call in to the normal handling code */
tcp6_ctlinput_with_port(ip6cp, port);
+ ip6cp->ip6c_off -= sizeof(struct udphdr);
}
#endif /* INET6 */