Hello,

On Mon, Sep 28, 2026 at 11:07:44AM +0200, alf wrote:
</snip>
> 
> output of screenshots of the vultr console parsed by OCR
> and hand edited by me:
> o8 0 0 0
> 110011557 41e622,,   0,   1 )  - >    e
> 1kernel: page fault trap, code=0          ,
> Stopped at      rn_match+0x44:  movzbl  0(%rdi,%rax,1),%eax
>    TID    PID    UID     PRfLAGS     PFLAGS  CPU  COMMAND
>  477789  49437      0    0x14000     0x200   0  softnet1
> *425356  21547      0    0x14000     0x200   2  softnet0
> rn_match(ffff80003165d010,ffff800000c3f600) at rn_match+0x44
> pfr_lookup_addr(ffff800016fdcfc0,ffff80003165d0e8,1) at pfr_lookup_addr+0xfb
> pfr_insert_kentry(ffff800016fdcfc0,ffff80003165d0e8,6a8f74d8) at 
> pfr_insert_ken
>  try+0x32
> pff_src_connlimit(ffff80003165d3f0) at pf_src_connlimit+0x20a
> pff_tcp_track_full(ffff80003165d300,ffff80003165d3f0,ffff80003165d3fe,ffff800031
> 65d254,0) at pf_tcp_track_full+0xcbc
> pf_test_state(ffff80003165d300,ffff80003165d3f0,ffff80003165d3fe) at 
> pf_test_st
> ate+0xb03
> pf_test(18,1,ffff800000057858,ffff80003165d578) at pf_test+0xfcd
> ip6_input_if(ffff80003165d578,ffff80003165d584,29,0,ffff800000057858,fffffff82
> bb4358) at ip6_input_if+0x1e9
> ipv6_input(ffff800000057858,fffffd80be095400,ffffffff82bb4358) at 
> ipv6_input+0x
> 42

    the problem is pf(4) updates overload table without holding a PF_LOCK().
    pf(4) holds state lock to find state, then it grabs reference and drops the
    state lock. however if state is created on behalf of rule with overload
    action, pf(4) must lock PF_LOCK() and eventually state table when calling
    to pf_src_connlimit(). 

patch below should help to make your firewall stable.

I tested the change with rules as follows:

table <minute>

pass all

block in on vio2
block return in quick on vio2 from <minute> to any
pass in on vio2 from 10.188.212.0/24 to any keep state (max-src-conn-rate 1/1, 
overload <minute>)


thanks for very helpful bug report. hope the patch below will help you.

regards
sashan

--------8<---------------8<---------------8<------------------8<--------
diff --git a/sys/net/pf.c b/sys/net/pf.c
index b465a77b487..e6c67842a07 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -757,9 +757,12 @@ pf_src_connlimit(struct pf_state **stp)
        int                      bad = 0;
        struct pf_src_node      *sn;
        u_int32_t                sn_conn;
+       int                      rv = 0;
+
+       PF_LOCK();
 
        if ((sn = pf_get_src_node((*stp), PF_SN_NONE)) == NULL)
-               return (0);
+               goto done;
 
        /*
         * Note: conn limit is bumped on SYN_SENT->ESTBLISHED
@@ -784,7 +787,7 @@ pf_src_connlimit(struct pf_state **stp)
        }
 
        if (!bad)
-               return (0);
+               goto done;
 
        if ((*stp)->rule.ptr->overload_tbl) {
                struct pfr_addr p;
@@ -822,6 +825,7 @@ pf_src_connlimit(struct pf_state **stp)
                        struct pf_state *st;
 
                        pf_status.lcounters[LCNT_OVERLOAD_FLUSH]++;
+                       PF_STATE_ENTER_READ();
                        RBT_FOREACH(st, pf_state_tree_id, &tree_id) {
                                sk = st->key[PF_SK_WIRE];
                                /*
@@ -844,6 +848,7 @@ pf_src_connlimit(struct pf_state **stp)
                                        killed++;
                                }
                        }
+                       PF_STATE_EXIT_READ();
                        if (pf_status.debug >= LOG_NOTICE)
                                addlog(", %u states killed", killed);
                }
@@ -854,7 +859,12 @@ pf_src_connlimit(struct pf_state **stp)
        /* kill this state */
        pf_update_state_timeout(*stp, PFTM_PURGE);
        pf_set_protostate(*stp, PF_PEER_BOTH, TCPS_CLOSED);
-       return (1);
+
+       rv = 1;
+done:
+       PF_UNLOCK();
+
+       return (rv);
 }
 
 int

Reply via email to