git: 25f04bf42786 - main - pf: do not free the caller's ruleset when attaching a table fails
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 12 Oct 2026 16:05:58 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=25f04bf42786806e2661b7a09c283b83f568380a
commit 25f04bf42786806e2661b7a09c283b83f568380a
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2026-10-12 08:00:11 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2026-10-12 13:08:59 +0000
pf: do not free the caller's ruleset when attaching a table fails
pfr_attach_table() takes a struct pf_ruleset * from its caller and, on its
own error paths, calls pfr_destroy_ktable() on the table it just made. That
drops the table's reference on the ruleset and calls
pf_remove_if_empty_ruleset(), which may unlink and free the anchor the
ruleset is embedded in -- and then pfr_attach_table() returns NULL to a
caller still holding the pointer.
Reached from userland with DIOCADDRULE and a PF_ADDR_DYNIFTL source, after
DIOCSETLIMIT has lowered PF_LIMIT_TABLES to one item of headroom: the first
pfr_create_ktable() takes it and the root table's fails.
Hold a table reference on the ruleset for the duration instead, so the
ruleset outlives every failure path inside pfr_attach_table() and stays the
caller's to remove.
OK sashan@
Obtained from: OpenBSD, gnezdo <gnezdo@openbsd.org>, e0a25791f4
Sponsored by: Rubicon Communications, LLC ("Netgate")
---
sys/netpfil/pf/pf_table.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/sys/netpfil/pf/pf_table.c b/sys/netpfil/pf/pf_table.c
index 7fc3b0a6380b..94ea5181ee74 100644
--- a/sys/netpfil/pf/pf_table.c
+++ b/sys/netpfil/pf/pf_table.c
@@ -2285,15 +2285,26 @@ pfr_attach_table(struct pf_kruleset *rs, char *name)
strlcpy(tbl.pfrt_anchor, ac->path, sizeof(tbl.pfrt_anchor));
kt = pfr_lookup_table(&tbl);
if (kt == NULL) {
+ /*
+ * Hold rs across the table creation below. A new table takes
+ * a reference on the ruleset, and pfr_destroy_ktable() drops
+ * it again on the failure paths, which lets
+ * pf_remove_if_empty_ruleset() free the anchor rs points into
+ * while our caller still holds rs.
+ */
+ rs->tables++;
kt = pfr_create_ktable(&tbl, time_second, 1);
- if (kt == NULL)
+ if (kt == NULL) {
+ rs->tables--;
return (NULL);
+ }
if (ac != NULL) {
bzero(tbl.pfrt_anchor, sizeof(tbl.pfrt_anchor));
rt = pfr_lookup_table(&tbl);
if (rt == NULL) {
rt = pfr_create_ktable(&tbl, 0, 1);
if (rt == NULL) {
+ rs->tables--;
pfr_destroy_ktable(kt, 0);
return (NULL);
}
@@ -2302,6 +2313,7 @@ pfr_attach_table(struct pf_kruleset *rs, char *name)
kt->pfrkt_root = rt;
}
pfr_insert_ktable(kt);
+ rs->tables--;
}
if (!kt->pfrkt_refcnt[PFR_REFCNT_RULE]++)
pfr_setflags_ktable(kt, kt->pfrkt_flags|PFR_TFLAG_REFERENCED);