git: 80af0a32e48b - main - tcp: Preserve borrowed ICMPv6 error mbufs
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 05 Oct 2026 13:23:23 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=80af0a32e48bff6ad97e768b16d2dbc01ac8d1c3
commit 80af0a32e48bff6ad97e768b16d2dbc01ac8d1c3
Author: Andrew Griffiths <andrew@calif.io>
AuthorDate: 2026-10-05 13:14:38 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-05 13:21:20 +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
---
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 1be803ed443f..4650e8035260 100644
--- a/sys/netinet/tcp_subr.c
+++ b/sys/netinet/tcp_subr.c
@@ -3392,25 +3392,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 */