Re: pf: u_int32_t conn counter underflow in pf_src_tree_remove_state()

Alexandr Nedvedicky <[email protected]>
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
Hello,

On Tue, Aug 18, 2026 at 04:16:51PM +0200, Alexander Bluhm wrote:
> 
> Is it sufficient to have one unlocked atomic operation together
> with a locked non-atomic?
> 
> thread A pf_src_connlimit
> thread B pf_src_tree_remove_state
> 
> B PF_LOCK
> B read sn->conn
> A atomic increment sn->conn
> B write sn->conn - 1
> B PF_UNLOCK
> 
> This does not work, you need atomic everywhere.

    of course you are right. I kind of forgot about race
    when there are two states where when one does transition
    to ESTABLSIHED state while the other state is being purged.

> 
> > +     atomic_inc_int((int *)&sn->conn);
> 
> Do we need the cast?  Address of u_int32_t should work with
> atomic_inc_int().

    perhaps not. I dropped the cast here.

> 
> The overflow check is reading the sn->conn value independent from the
> atomic increment.  Something like this is better:
> 
> 	uint32_t sn_conn;
> 
> 	sn_conn = atomic_inc_int_nv(&sn->conn);
    do you mind if I do '(u_int32_t)atomic_inc_int_nv(..);'


updated diff is below.

thanks and
regards
sashan

--------8<---------------8<-----------------8<--------
diff --git a/sys/net/pf.c b/sys/net/pf.c
index 0fd00c0dbf3..5ce7c1226a1 100644
--- a/sys/net/pf.c
+++ b/sys/net/pf.c
@@ -756,16 +756,23 @@ pf_src_connlimit(struct pf_state **stp)
 {
 	int			 bad = 0;
 	struct pf_src_node	*sn;
+	u_int32_t		 sn_conn;
 
 	if ((sn = pf_get_src_node((*stp), PF_SN_NONE)) == NULL)
 		return (0);
 
-	sn->conn++;
+	/*
+	 * Note: conn limit is bumped on SYN_SENT->ESTBLISHED
+	 * state transition. packet does not hold any locks
+	 * when running here, therefore atomic is needed.
+	 */
+	sn_conn = (u_int32_t) atomic_inc_int_nv(&sn->conn);
+
 	(*stp)->src.tcp_est = 1;
 	pf_add_threshold(&sn->conn_rate);
 
 	if ((*stp)->rule.ptr->max_src_conn &&
-	    (*stp)->rule.ptr->max_src_conn < sn->conn) {
+	    (*stp)->rule.ptr->max_src_conn < sn_conn) {
 		pf_status.lcounters[LCNT_SRCCONN]++;
 		bad++;
 	}
@@ -2054,7 +2061,7 @@ pf_src_tree_remove_state(struct pf_state *st)
 	while ((sni = SLIST_FIRST(&st->src_nodes)) != NULL) {
 		SLIST_REMOVE_HEAD(&st->src_nodes, next);
 		if (st->src.tcp_est)
-			--sni->sn->conn;
+			atomic_dec_int(&sni->sn->conn);
 		if (--sni->sn->states == 0) {
 			timeout = st->rule.ptr->timeout[PFTM_SRC_NODE];
 			if (!timeout)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.