untrusted comment: verify with openbsd-74-base.pub
RWRoyQmAD08ajRQyxUXGRdaJIVDydrrfAmdidIDLmAQPKc4cUuKzIOkWvMCcWWS3LFJBN2JishP4g4qDbOiqlcRqvLU21Q1/vAs=

OpenBSD 7.4 errata 009, December 10, 2023:

A race condition between pf(4)'s processing of packets and expiration of
packet states may cause a kernel panic.

Apply by doing:
    signify -Vep /etc/signify/openbsd-74-base.pub -x 009_pf.patch.sig \
        -m - | (cd /usr/src && patch -p0)

And then rebuild and install a new kernel:
    KK=`sysctl -n kern.osversion | cut -d# -f1`
    cd /usr/src/sys/arch/`machine`/compile/$KK
    make obj
    make config
    make
    make install

Index: sys/net/pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.1186
diff -u -p -r1.1186 pf.c
--- sys/net/pf.c	8 Sep 2023 13:40:52 -0000	1.1186
+++ sys/net/pf.c	4 Dec 2023 21:04:47 -0000
@@ -469,6 +469,15 @@ pf_state_list_remove(struct pf_state_lis
 	pf_state_unref(st); /* list no longer references the state */
 }
 
+void
+pf_update_state_timeout(struct pf_state *st, int to)
+{
+	mtx_enter(&st->mtx);
+	if (st->timeout != PFTM_UNLINKED)
+		st->timeout = to;
+	mtx_leave(&st->mtx);
+}
+
 int
 pf_src_connlimit(struct pf_state **stp)
 {
@@ -549,7 +558,7 @@ pf_src_connlimit(struct pf_state **stp)
 				    ((*stp)->rule.ptr->flush &
 				    PF_FLUSH_GLOBAL ||
 				    (*stp)->rule.ptr == st->rule.ptr)) {
-					st->timeout = PFTM_PURGE;
+					pf_update_state_timeout(st, PFTM_PURGE);
 					pf_set_protostate(st, PF_PEER_BOTH,
 					    TCPS_CLOSED);
 					killed++;
@@ -563,7 +572,7 @@ pf_src_connlimit(struct pf_state **stp)
 	}
 
 	/* kill this state */
-	(*stp)->timeout = PFTM_PURGE;
+	pf_update_state_timeout(*stp, PFTM_PURGE);
 	pf_set_protostate(*stp, PF_PEER_BOTH, TCPS_CLOSED);
 	return (1);
 }
@@ -1758,10 +1767,13 @@ pf_remove_state(struct pf_state *st)
 {
 	PF_ASSERT_LOCKED();
 
-	if (st->timeout == PFTM_UNLINKED)
+	mtx_enter(&st->mtx);
+	if (st->timeout == PFTM_UNLINKED) {
+		mtx_leave(&st->mtx);
 		return;
-
+	}
 	st->timeout = PFTM_UNLINKED;
+	mtx_leave(&st->mtx);
 
 	/* handle load balancing related tasks */
 	pf_postprocess_addr(st);
@@ -1816,7 +1828,8 @@ pf_remove_divert_state(struct pf_state_k
 				    sist->dst.state < TCPS_FIN_WAIT_2) {
 					pf_set_protostate(sist, PF_PEER_BOTH,
 					    TCPS_TIME_WAIT);
-					sist->timeout = PFTM_TCP_CLOSED;
+					pf_update_state_timeout(sist,
+					    PFTM_TCP_CLOSED);
 					sist->expire = getuptime();
 				}
 				sist->state_flags |= PFSTATE_INP_UNLINKED;
@@ -5036,18 +5049,18 @@ pf_tcp_track_full(struct pf_pdesc *pd, s
 		(*stp)->expire = getuptime();
 		if (src->state >= TCPS_FIN_WAIT_2 &&
 		    dst->state >= TCPS_FIN_WAIT_2)
-			(*stp)->timeout = PFTM_TCP_CLOSED;
+			pf_update_state_timeout(*stp, PFTM_TCP_CLOSED);
 		else if (src->state >= TCPS_CLOSING &&
 		    dst->state >= TCPS_CLOSING)
-			(*stp)->timeout = PFTM_TCP_FIN_WAIT;
+			pf_update_state_timeout(*stp, PFTM_TCP_FIN_WAIT);
 		else if (src->state < TCPS_ESTABLISHED ||
 		    dst->state < TCPS_ESTABLISHED)
-			(*stp)->timeout = PFTM_TCP_OPENING;
+			pf_update_state_timeout(*stp, PFTM_TCP_OPENING);
 		else if (src->state >= TCPS_CLOSING ||
 		    dst->state >= TCPS_CLOSING)
-			(*stp)->timeout = PFTM_TCP_CLOSING;
+			pf_update_state_timeout(*stp, PFTM_TCP_CLOSING);
 		else
-			(*stp)->timeout = PFTM_TCP_ESTABLISHED;
+			pf_update_state_timeout(*stp, PFTM_TCP_ESTABLISHED);
 
 		/* Fall through to PASS packet */
 	} else if ((dst->state < TCPS_SYN_SENT ||
@@ -5229,18 +5242,18 @@ pf_tcp_track_sloppy(struct pf_pdesc *pd,
 	(*stp)->expire = getuptime();
 	if (src->state >= TCPS_FIN_WAIT_2 &&
 	    dst->state >= TCPS_FIN_WAIT_2)
-		(*stp)->timeout = PFTM_TCP_CLOSED;
+		pf_update_state_timeout(*stp, PFTM_TCP_CLOSED);
 	else if (src->state >= TCPS_CLOSING &&
 	    dst->state >= TCPS_CLOSING)
-		(*stp)->timeout = PFTM_TCP_FIN_WAIT;
+		pf_update_state_timeout(*stp, PFTM_TCP_FIN_WAIT);
 	else if (src->state < TCPS_ESTABLISHED ||
 	    dst->state < TCPS_ESTABLISHED)
-		(*stp)->timeout = PFTM_TCP_OPENING;
+		pf_update_state_timeout(*stp, PFTM_TCP_OPENING);
 	else if (src->state >= TCPS_CLOSING ||
 	    dst->state >= TCPS_CLOSING)
-		(*stp)->timeout = PFTM_TCP_CLOSING;
+		pf_update_state_timeout(*stp, PFTM_TCP_CLOSING);
 	else
-		(*stp)->timeout = PFTM_TCP_ESTABLISHED;
+		pf_update_state_timeout(*stp, PFTM_TCP_ESTABLISHED);
 
 	return (PF_PASS);
 }
@@ -5377,7 +5390,7 @@ pf_test_state(struct pf_pdesc *pd, struc
 					addlog("\n");
 				}
 				/* XXX make sure it's the same direction ?? */
-				(*stp)->timeout = PFTM_PURGE;
+				pf_update_state_timeout(*stp, PFTM_PURGE);
 				pf_state_unref(*stp);
 				*stp = NULL;
 				pf_mbuf_link_inpcb(pd->m, inp);
@@ -5417,9 +5430,9 @@ pf_test_state(struct pf_pdesc *pd, struc
 		(*stp)->expire = getuptime();
 		if (src->state == PFUDPS_MULTIPLE &&
 		    dst->state == PFUDPS_MULTIPLE)
-			(*stp)->timeout = PFTM_UDP_MULTIPLE;
+			pf_update_state_timeout(*stp, PFTM_UDP_MULTIPLE);
 		else
-			(*stp)->timeout = PFTM_UDP_SINGLE;
+			pf_update_state_timeout(*stp, PFTM_UDP_SINGLE);
 		break;
 	default:
 		/* update states */
@@ -5432,9 +5445,9 @@ pf_test_state(struct pf_pdesc *pd, struc
 		(*stp)->expire = getuptime();
 		if (src->state == PFOTHERS_MULTIPLE &&
 		    dst->state == PFOTHERS_MULTIPLE)
-			(*stp)->timeout = PFTM_OTHER_MULTIPLE;
+			pf_update_state_timeout(*stp, PFTM_OTHER_MULTIPLE);
 		else
-			(*stp)->timeout = PFTM_OTHER_SINGLE;
+			pf_update_state_timeout(*stp, PFTM_OTHER_SINGLE);
 		break;
 	}
 
@@ -5585,7 +5598,7 @@ pf_test_state_icmp(struct pf_pdesc *pd, 
 			return (ret);
 
 		(*stp)->expire = getuptime();
-		(*stp)->timeout = PFTM_ICMP_ERROR_REPLY;
+		pf_update_state_timeout(*stp, PFTM_ICMP_ERROR_REPLY);
 
 		/* translate source/destination address, if necessary */
 		if ((*stp)->key[PF_SK_WIRE] != (*stp)->key[PF_SK_STACK]) {