On 2026/03/12 23:59, [email protected] wrote:
> ------------------------------------------
> Description of issue
> ------------------------------------------
> When pfctl's ruleset optimizer (enabled by default) fails to create an
> __automatic_* table — for example because the kernel table limit is
> exhausted — the error is printed but execution continues and the
> incomplete ruleset is committed to the kernel via DIOCXCOMMIT. This
> leaves the firewall in a broken state, silently passing or mishandling
> traffic that the intended ruleset would have blocked.
> 
> The root cause is in pfctl_load_ruleset() in sbin/pfctl/pfctl.c. The
> return value of pfctl_optimize_ruleset() is discarded, so its failure
> does not trigger the existing error recovery path (goto error) that
> would instead issue DIOCXROLLBACK and abort the load.
> 
> Every other fallible call in pfctl_load_ruleset() correctly checks its
> return value and branches to the error label on failure. The optimizer
> call is the sole exception.
> 
> ------------------------------------------
> Reproduction
> ------------------------------------------
> Configure a pf.conf whose ruleset causes the optimizer to generate
> more tables than the kernel table limit allows (default 1000). I was able
> reproduce the error condition with a pf.conf file that defines 500 tables
> along with one or more rules that result in automatically  generated
> tables. Automatically generated tables are observed as the proximate
> trigger for the error condition in this case as seen in the verbose log
> output. While reloading the ruleset, the default 1000 table limit is exceeded.
> 
> Observed output (with instrumented pfctl logging DIOCX* calls):
> 
> # /usr/obj/sbin/pfctl/pfctl-debug-trans -vv -f /etc/pf.conf.bad
> ...
> table <blocked_ips> persist file "/etc/pf_table_blocked.conf"
> table <__automatic_71e4967e_0> const { 142.251.150.119 142.251.151.119 
> 142.251.152.119 142.251.153.119 142.251.154.119 142.251.155.119 
> 142.251.156.119 142.251.157.119 9.9.9.9 }
> pfctl-debug-trans: failed to create table __automatic_71e4967e_1 in : Cannot 
> allocate memory
> [trans] >>> DIOCXCOMMIT: 2 element(s)
> [trans]       [0] RULESET anchor="(root)"
> [trans]       [1] TABLE   anchor="(root)"
> [trans] <<< DIOCXCOMMIT OK
> 
> The incomplete ruleset is committed. Rules referencing the missing
> __automatic_* table operate incorrectly or not at all, resulting in
> traffic bypassing the intended firewall policy.
> 
> ------------------------------------------
> Expected behavior
> ------------------------------------------
> The failure of pfctl_optimize_ruleset() should abort the ruleset load.
> The kernel transaction should be rolled back via DIOCXROLLBACK, leaving
> the previously active ruleset intact.
> 
> ------------------------------------------
> Patch
> ------------------------------------------
> --- pfctl.c.orig        Fri Mar 13 05:23:23 2026
> +++ pfctl.c     Fri Mar 13 05:25:44 2026
> @@ -1527,7 +1527,8 @@

the line numbers are nonsense. llm-generated diff I guess.

>         }
> 
>         if (pf->optimize)
> -               pfctl_optimize_ruleset(pf, rs);
> +               if ((error = pfctl_optimize_ruleset(pf, rs)) != 0)
> +               goto error;
> 
>         while ((r = TAILQ_FIRST(rs->rules.active.ptr)) != NULL) {
>                 TAILQ_REMOVE(rs->rules.active.ptr, r, entries);

no need to be different than the other checks in the file, and
indentation is wrong.

Index: pfctl.c
===================================================================
RCS file: /cvs/src/sbin/pfctl/pfctl.c,v
diff -u -p -r1.400 pfctl.c
--- pfctl.c     3 Feb 2026 10:25:28 -0000       1.400
+++ pfctl.c     13 Mar 2026 10:57:09 -0000
@@ -1988,7 +1988,8 @@ pfctl_load_ruleset(struct pfctl *pf, cha
        }
 
        if (pf->optimize)
-               pfctl_optimize_ruleset(pf, rs);
+               if (error = pfctl_optimize_ruleset(pf, rs))
+                       goto error;
 
        while ((r = TAILQ_FIRST(rs->rules.active.ptr)) != NULL) {
                TAILQ_REMOVE(rs->rules.active.ptr, r, entries);

Reply via email to