Hello, diff below has been posted to bugs@ ~week [1] ago. Diff fixes long lasting issue I was scratching my head around for several months.
Recently I've shared my itch with bluhm@ who pointed out that `sc_st_mtx` mutex is not sufficient protection. We also need to grab pf_state::mtx when inserting/removing/moving particular state on sync queues. diff below makes sure pfsync always grab a pf_state::mtx when it moves state around its queues. Hrvoje confirms diff fixes panic he was able to induce on his test bed under stress load. OK to commit? thanks and regards sashan [1] https://marc.info/?l=openbsd-bugs&m=168367101726447&w=2 --------8<---------------8<---------------8<------------------8<-------- diff --git a/sys/net/if_pfsync.c b/sys/net/if_pfsync.c index 822b4211d0f..811d9d59666 100644 --- a/sys/net/if_pfsync.c +++ b/sys/net/if_pfsync.c @@ -1362,14 +1362,17 @@ pfsync_grab_snapshot(struct pfsync_snapshot *sn, struct pfsync_softc *sc) while ((st = TAILQ_FIRST(&sc->sc_qs[q])) != NULL) { TAILQ_REMOVE(&sc->sc_qs[q], st, sync_list); + mtx_enter(&st->mtx); if (st->snapped == 0) { TAILQ_INSERT_TAIL(&sn->sn_qs[q], st, sync_snap); st->snapped = 1; + mtx_leave(&st->mtx); } else { /* * item is on snapshot list already, so we can * skip it now. */ + mtx_leave(&st->mtx); pf_state_unref(st); } } @@ -1422,11 +1425,13 @@ pfsync_drop_snapshot(struct pfsync_snapshot *sn) continue; while ((st = TAILQ_FIRST(&sn->sn_qs[q])) != NULL) { + mtx_enter(&st->mtx); KASSERT(st->sync_state == q); KASSERT(st->snapped == 1); TAILQ_REMOVE(&sn->sn_qs[q], st, sync_snap); st->sync_state = PFSYNC_S_NONE; st->snapped = 0; + mtx_leave(&st->mtx); pf_state_unref(st); } } @@ -1665,6 +1670,7 @@ pfsync_sendout(void) count = 0; while ((st = TAILQ_FIRST(&sn.sn_qs[q])) != NULL) { + mtx_enter(&st->mtx); TAILQ_REMOVE(&sn.sn_qs[q], st, sync_snap); KASSERT(st->sync_state == q); KASSERT(st->snapped == 1); @@ -1672,6 +1678,7 @@ pfsync_sendout(void) st->snapped = 0; pfsync_qs[q].write(st, m->m_data + offset); offset += pfsync_qs[q].len; + mtx_leave(&st->mtx); pf_state_unref(st); count++; @@ -1725,8 +1732,6 @@ pfsync_insert_state(struct pf_state *st) ISSET(st->state_flags, PFSTATE_NOSYNC)) return; - KASSERT(st->sync_state == PFSYNC_S_NONE); - if (sc->sc_len == PFSYNC_MINPKT) timeout_add_sec(&sc->sc_tmo, 1); @@ -2221,6 +2226,7 @@ pfsync_q_ins(struct pf_state *st, int q) panic("pfsync pkt len is too low %zd", sc->sc_len); do { mtx_enter(&sc->sc_st_mtx); + mtx_enter(&st->mtx); /* * There are either two threads trying to update the @@ -2228,6 +2234,7 @@ pfsync_q_ins(struct pf_state *st, int q) * (is on snapshot queue). */ if (st->sync_state != PFSYNC_S_NONE) { + mtx_leave(&st->mtx); mtx_leave(&sc->sc_st_mtx); break; } @@ -2240,6 +2247,7 @@ pfsync_q_ins(struct pf_state *st, int q) sclen = atomic_add_long_nv(&sc->sc_len, nlen); if (sclen > sc->sc_if.if_mtu) { atomic_sub_long(&sc->sc_len, nlen); + mtx_leave(&st->mtx); mtx_leave(&sc->sc_st_mtx); pfsync_sendout(); continue; @@ -2249,6 +2257,7 @@ pfsync_q_ins(struct pf_state *st, int q) TAILQ_INSERT_TAIL(&sc->sc_qs[q], st, sync_list); st->sync_state = q; + mtx_leave(&st->mtx); mtx_leave(&sc->sc_st_mtx); } while (0); } @@ -2260,6 +2269,7 @@ pfsync_q_del(struct pf_state *st) int q; mtx_enter(&sc->sc_st_mtx); + mtx_enter(&st->mtx); q = st->sync_state; /* * re-check under mutex @@ -2267,6 +2277,7 @@ pfsync_q_del(struct pf_state *st) * too late, the state is being just processed/dispatched to peer. */ if ((q == PFSYNC_S_NONE) || (st->snapped)) { + mtx_leave(&st->mtx); mtx_leave(&sc->sc_st_mtx); return; } @@ -2275,6 +2286,7 @@ pfsync_q_del(struct pf_state *st) if (TAILQ_EMPTY(&sc->sc_qs[q])) atomic_sub_long(&sc->sc_len, sizeof (struct pfsync_subheader)); st->sync_state = PFSYNC_S_NONE; + mtx_leave(&st->mtx); mtx_leave(&sc->sc_st_mtx); pf_state_unref(st);