git: ccb19b359aae - main - pf: check extension header length.

From: Kristof Provost <kp_at_FreeBSD.org>
Date: Mon, 12 Oct 2026 16:05:59 UTC
The branch main has been updated by kp:

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

commit ccb19b359aae4b7e7315ad61d33d9a35d85849d1
Author:     Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-10-12 12:25:07 +0000
Commit:     Kristof Provost <kp@FreeBSD.org>
CommitDate: 2026-10-12 13:08:59 +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")
---
 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 3dcb0c29e62a..46f9f044accb 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -10962,7 +10962,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);
@@ -11073,9 +11073,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: