The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=31131a9d6a20e62de43550531aea3a31ea8103cc
commit 31131a9d6a20e62de43550531aea3a31ea8103cc Author: Kristof Provost <k...@freebsd.org> AuthorDate: 2025-07-30 15:14:26 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-08-05 22:27:15 +0000 pf: allocate pf_kanchor from a pool Add a pool for the allocation of the pf_anchor struct. It was possible to exhaust kernel memory by repeatedly calling pfioctl DIOCXBEGIN with different anchor names. OK bluhm@ Reported-by: syzbot+9dd98cbce69e26f0f...@syzkaller.appspotmail.com Obtained from: OpenBSD, mbuhl <mb...@openbsd.org>, fa90ac5c78 Obtained from: OpenBSD, mbuhl <mb...@openbsd.org>, c259202341 Sponsored by: Rubicon Communications, LLC ("Netgate") --- sbin/pfctl/pfctl.c | 1 + sys/net/pfvar.h | 2 ++ sys/netpfil/pf/pf.c | 8 ++++++++ sys/netpfil/pf/pf.h | 3 ++- sys/netpfil/pf/pf_ioctl.c | 1 + sys/netpfil/pf/pf_norm.c | 1 + sys/netpfil/pf/pf_ruleset.c | 8 ++++---- 7 files changed, 19 insertions(+), 5 deletions(-) diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index 9aa50a73ba04..a96eed7fc94a 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -183,6 +183,7 @@ static const struct { { "src-nodes", PF_LIMIT_SRC_NODES }, { "frags", PF_LIMIT_FRAGS }, { "table-entries", PF_LIMIT_TABLE_ENTRIES }, + { "anchors", PF_LIMIT_ANCHORS }, { NULL, 0 } }; diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 8dae95c2cc2e..c933ff395992 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -2338,6 +2338,8 @@ VNET_DECLARE(uma_zone_t, pf_udp_mapping_z); #define V_pf_udp_mapping_z VNET(pf_udp_mapping_z) VNET_DECLARE(uma_zone_t, pf_state_scrub_z); #define V_pf_state_scrub_z VNET(pf_state_scrub_z) +VNET_DECLARE(uma_zone_t, pf_anchor_z); +#define V_pf_anchor_z VNET(pf_anchor_z) extern void pf_purge_thread(void *); extern void pf_unload_vnet_purge(void); diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 4801b3e2c766..408d0d3c96e3 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -1254,6 +1254,14 @@ pf_initialize(void) MTX_DEF | MTX_DUPOK); } + /* Anchors */ + V_pf_anchor_z = uma_zcreate("pf anchors", + sizeof(struct pf_kanchor), NULL, NULL, NULL, NULL, + UMA_ALIGN_PTR, 0); + V_pf_limits[PF_LIMIT_ANCHORS].zone = V_pf_anchor_z; + uma_zone_set_max(V_pf_anchor_z, PF_ANCHOR_HIWAT); + uma_zone_set_warning(V_pf_anchor_z, "PF anchor limit reached"); + /* ALTQ */ TAILQ_INIT(&V_pf_altqs[0]); TAILQ_INIT(&V_pf_altqs[1]); diff --git a/sys/netpfil/pf/pf.h b/sys/netpfil/pf/pf.h index cfff58064922..a41443fb9404 100644 --- a/sys/netpfil/pf/pf.h +++ b/sys/netpfil/pf/pf.h @@ -120,7 +120,7 @@ enum { enum { PF_NOPFROUTE, PF_FASTROUTE, PF_ROUTETO, PF_DUPTO, PF_REPLYTO }; enum { PF_LIMIT_STATES, PF_LIMIT_SRC_NODES, PF_LIMIT_FRAGS, - PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_MAX }; + PF_LIMIT_TABLE_ENTRIES, PF_LIMIT_ANCHORS, PF_LIMIT_MAX }; #define PF_POOL_IDMASK 0x0f enum { PF_POOL_NONE, PF_POOL_BITMASK, PF_POOL_RANDOM, PF_POOL_SRCHASH, PF_POOL_ROUNDROBIN }; @@ -490,6 +490,7 @@ struct pf_osfp_ioctl { #define PF_ANCHOR_NAME_SIZE 64 #define PF_ANCHOR_MAXPATH (MAXPATHLEN - PF_ANCHOR_NAME_SIZE - 1) +#define PF_ANCHOR_HIWAT 512 #define PF_OPTIMIZER_TABLE_PFX "__automatic_" struct pf_rule { diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 08ae2057653b..935747d9f58a 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -331,6 +331,7 @@ pfattach_vnet(void) V_pf_limits[PF_LIMIT_STATES].limit = PFSTATE_HIWAT; V_pf_limits[PF_LIMIT_SRC_NODES].limit = PFSNODE_HIWAT; + V_pf_limits[PF_LIMIT_ANCHORS].limit = PF_ANCHOR_HIWAT; RB_INIT(&V_pf_anchors); pf_init_kruleset(&pf_main_ruleset); diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c index 8cea9557633c..2b822dca55b5 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -118,6 +118,7 @@ VNET_DEFINE_STATIC(uma_zone_t, pf_frnode_z); #define V_pf_frnode_z VNET(pf_frnode_z) VNET_DEFINE_STATIC(uma_zone_t, pf_frag_z); #define V_pf_frag_z VNET(pf_frag_z) +VNET_DEFINE(uma_zone_t, pf_anchor_z); TAILQ_HEAD(pf_fragqueue, pf_fragment); TAILQ_HEAD(pf_cachequeue, pf_fragment); diff --git a/sys/netpfil/pf/pf_ruleset.c b/sys/netpfil/pf/pf_ruleset.c index 43b51f2933f4..7f21b3f8fc47 100644 --- a/sys/netpfil/pf/pf_ruleset.c +++ b/sys/netpfil/pf/pf_ruleset.c @@ -238,7 +238,7 @@ pf_create_kanchor(struct pf_kanchor *parent, const char *aname) ((parent != NULL) && (strlen(parent->path) >= PF_ANCHOR_MAXPATH))) return (NULL); - anchor = rs_malloc(sizeof(*anchor)); + anchor = uma_zalloc(V_pf_anchor_z, M_NOWAIT | M_ZERO); if (anchor == NULL) return (NULL); @@ -259,7 +259,7 @@ pf_create_kanchor(struct pf_kanchor *parent, const char *aname) printf("%s: RB_INSERT1 " "'%s' '%s' collides with '%s' '%s'\n", __func__, anchor->path, anchor->name, dup->path, dup->name); - rs_free(anchor); + uma_zfree(V_pf_anchor_z, anchor); return (NULL); } @@ -273,7 +273,7 @@ pf_create_kanchor(struct pf_kanchor *parent, const char *aname) anchor->name, dup->path, dup->name); RB_REMOVE(pf_kanchor_global, &V_pf_anchors, anchor); - rs_free(anchor); + uma_zfree(V_pf_anchor_z, anchor); return (NULL); } } @@ -350,7 +350,7 @@ pf_remove_if_empty_kruleset(struct pf_kruleset *ruleset) if ((parent = ruleset->anchor->parent) != NULL) RB_REMOVE(pf_kanchor_node, &parent->children, ruleset->anchor); - rs_free(ruleset->anchor); + uma_zfree(V_pf_anchor_z, ruleset->anchor); if (parent == NULL) return; ruleset = &parent->ruleset;