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;

Reply via email to