The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=dda88af8fa4eb1455a6e899434936c4ddeaf526d
commit dda88af8fa4eb1455a6e899434936c4ddeaf526d Author: Kristof Provost <k...@freebsd.org> AuthorDate: 2025-05-26 09:55:12 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-06-06 11:16:00 +0000 pf: limit how many headers we look at Limit the nested header chain for IPv6 extensions headers and for authentication headers in the IPv4 case. This prevents spending excessive cpu time on crafted packets. OK henning@ Obtained from: OpenBSD, bluhm <bl...@openbsd.org>, 2e5bc81177 Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D50659 --- sys/netpfil/pf/pf.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 0cfb728c3eb5..f1b04a96590b 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -180,6 +180,8 @@ VNET_DEFINE(u_int32_t, ticket_altqs_inactive); VNET_DEFINE(int, altqs_inactive_open); VNET_DEFINE(u_int32_t, ticket_pabuf); +static const int PF_HDR_LIMIT = 20; /* arbitrary limit */ + VNET_DEFINE(SHA512_CTX, pf_tcp_secret_ctx); #define V_pf_tcp_secret_ctx VNET(pf_tcp_secret_ctx) VNET_DEFINE(u_char, pf_tcp_secret[16]); @@ -9698,6 +9700,7 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason) { struct ah ext; u_int32_t hlen, end; + int hdr_cnt; hlen = h->ip_hl << 2; if (hlen < sizeof(struct ip) || hlen > ntohs(h->ip_len)) { @@ -9710,7 +9713,7 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason) /* stop walking over non initial fragments */ if ((h->ip_off & htons(IP_OFFMASK)) != 0) return (PF_PASS); - for (;;) { + for (hdr_cnt = 0; hdr_cnt < PF_HDR_LIMIT; hdr_cnt++) { switch (pd->proto) { case IPPROTO_AH: /* fragments may be short */ @@ -9729,6 +9732,9 @@ pf_walk_header(struct pf_pdesc *pd, struct ip *h, u_short *reason) return (PF_PASS); } } + DPFPRINTF(PF_DEBUG_MISC, ("IPv4 nested authentication header limit")); + REASON_SET(reason, PFRES_IPOPTIONS); + return (PF_DROP); } #ifdef INET6 @@ -9801,14 +9807,13 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason) struct ip6_ext ext; struct ip6_rthdr rthdr; uint32_t end; - int hdr_cnt = 0, fraghdr_cnt = 0, rthdr_cnt = 0; + int hdr_cnt, fraghdr_cnt = 0, rthdr_cnt = 0; pd->off += sizeof(struct ip6_hdr); end = pd->off + ntohs(h->ip6_plen); pd->fragoff = pd->extoff = pd->jumbolen = 0; pd->proto = h->ip6_nxt; - for (;;) { - hdr_cnt++; + for (hdr_cnt = 0; hdr_cnt < PF_HDR_LIMIT; hdr_cnt++) { switch (pd->proto) { case IPPROTO_FRAGMENT: if (fraghdr_cnt++) { @@ -9863,7 +9868,7 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason) /* FALLTHROUGH */ case IPPROTO_HOPOPTS: /* RFC2460 4.1: Hop-by-Hop only after IPv6 header */ - if (pd->proto == IPPROTO_HOPOPTS && hdr_cnt > 1) { + if (pd->proto == IPPROTO_HOPOPTS && hdr_cnt > 0) { DPFPRINTF(PF_DEBUG_MISC, ("IPv6 hopopts not first")); REASON_SET(reason, PFRES_IPOPTIONS); return (PF_DROP); @@ -9922,6 +9927,9 @@ pf_walk_header6(struct pf_pdesc *pd, struct ip6_hdr *h, u_short *reason) return (PF_PASS); } } + DPFPRINTF(PF_DEBUG_MISC, ("IPv6 nested extension header limit")); + REASON_SET(reason, PFRES_IPOPTIONS); + return (PF_DROP); } #endif /* INET6 */