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