git: a2a0e5e0e38f - stable/15 - pf: check extension header length.

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Tue, 20 Oct 2026 07:24:35 UTC
The branch stable/15 has been updated by kp:

URL: https://cgit.FreeBSD.org/src/commit/?id=a2a0e5e0e38fa9791f2966e0430b8754530d6b72

commit a2a0e5e0e38fa9791f2966e0430b8754530d6b72
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-10-12 12:25:07 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2026-10-20 07:22:50 +0000

    pf: check extension header length.
    
    pf_walk_header6() advances the packet offset for each IPv6 extension
    header.  It was not checked that this does not exceed the packet
    length if no next header was processed.  When used with af-to rules,
    this could trigger a panic: m_copydata: null mbuf.
    
    from Acts1631; OK henning@ sashan@
    
    MFC after:      1 week
    Obtained from:  OpenBSD, bluhm <bluhm@openbsd.org>, 6d56112588
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    
    (cherry picked from commit ccb19b359aae4b7e7315ad61d33d9a35d85849d1)
---
 sys/netpfil/pf/pf.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index 4a4dd209b7c2..ea081fa90b34 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -10232,7 +10232,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
 	struct ip6_ext		 ext;
 	struct icmp6_hdr	 icmp6;
 	struct ip6_rthdr	 rthdr;
-	uint32_t		 end;
+	uint32_t		 end, extlen;
 	int			 hdr_cnt, fraghdr_cnt = 0, rthdr_cnt = 0;
 
 	pd->off += sizeof(struct ip6_hdr);
@@ -10343,9 +10343,20 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason)
 				return (PF_DROP);
 			}
 			if (pd->proto == IPPROTO_AH)
-				pd->off += (ext.ip6e_len + 2) * 4;
+				extlen = (ext.ip6e_len + 2) * 4;
 			else
-				pd->off += (ext.ip6e_len + 1) * 8;
+				extlen = (ext.ip6e_len + 1) * 8;
+			if (end < pd->off + extlen) {
+				if (pd->fragoff != 0) {
+					pd->off = pd->fragoff;
+					pd->proto = IPPROTO_FRAGMENT;
+					return (PF_PASS);
+				}
+				DPFPRINTF(PF_DEBUG_MISC, "IPv6 long exthdr");
+				REASON_SET(reason, PFRES_SHORT);
+				return (PF_DROP);
+			}
+			pd->off += extlen;
 			pd->proto = ext.ip6e_nxt;
 			break;
 		case IPPROTO_ICMPV6: