On Mon, Dec 05, 2022 at 12:45:29AM +0100, Alexandr Nedvedicky wrote: > Hello, > > > On Sat, Dec 03, 2022 at 09:53:45AM +1000, David Gwynne wrote: > > we (mostly sashan@ and me) have a problem where pfsync can be holding a > > reference to a pf_state that pf has decided to purge, and then pfsync > > crashes because it tries to read the pf_state_key parts of the state, > > but they been removed by the purge process. > > > > the background to this is that pf_state structures don't contain ip > > addresses, protocols, ports, etc. that information is stored in a > > pf_state_key struct, which is used to wire a state into the state table. > > when pfsync wants to export information about a state, particularly the > > addresses on it, it needs the pf_state_key struct to read from. > > > > the pf purge code current assumes that when a state is removed from the > > state table, the pf_state_key structs that are used to wire it in can > > also be removed and freed. this has obvious bad consequences for pfsync, > > which could still be holding a reference to the pf_state struct with the > > intention of reading from it. > > > > this diff tweaks the lifetime of pf_state_structs so they at least match > > the lifetime of pf_states. > > > > just so i'm clear, the main idea is that once pf_state_insert succeeds, > > a pf_state struct will always have pf_state_key structs hanging off it. > > it's only after the pf_state struct itself is being torn down that its > > references to pf_state_keys are released. if you're holding a > > pf_state reference, you can use that to access the pf_state_key > > structs hanging off it. > > > > this is largely accomplished by taking more advantage of the pf_state > > and pf_state_key refcnts. pf_states get properly accounted references to > > the pf_state_keys, and the state table gets its own reference counts to > > the pf_state_keys too. when a state is removed from the state table, the > > state table reference counts drop. however, it's not until all pf_state > > references drop that pf_state will give up its references to the > > pf_states. > > > > this is a very new diff, but i've been kicking it around with pfsync a > > bit and the pf_state_export crashes i used to get are gone now. > > > > if you're going to test the things to look for are if NAT still works, > > and if the pfstate and pfstkey pool numbers still look right. > > all above makes sense to me. I'm fine with the approach. > I think I could just one single glitch here in pf_state_key_attach(): > > > > > Index: pf.c > > =================================================================== > > RCS file: /cvs/src/sys/net/pf.c,v > > retrieving revision 1.1156 > > diff -u -p -r1.1156 pf.c > > --- pf.c 25 Nov 2022 20:27:53 -0000 1.1156 > > +++ pf.c 2 Dec 2022 23:20:36 -0000 > </snip> > > @@ -766,44 +777,52 @@ pf_state_key_attach(struct pf_state_key > > /* remove late or sks can go away */ > > olds = si->s; > > } else { > > - pool_put(&pf_state_key_pl, sk); > > - return (-1); /* collision! */ > > + pf_state_key_unref(sk); > > + return (NULL); /* collision! */ > > } > > } > > - pool_put(&pf_state_key_pl, sk); > > - s->key[idx] = cur; > > - } else > > - s->key[idx] = sk; > > + } > > + > > + /* reuse the existing state key */ > > + pf_state_key_unref(sk); > > + sk = cur; > > + } > > > > if ((si = pool_get(&pf_state_item_pl, PR_NOWAIT)) == NULL) { > > - pf_state_key_detach(s, idx); > > - return (-1); > > + if (TAILQ_EMPTY(&sk->states)) { > > + RB_REMOVE(pf_state_tree, &pf_statetbl, sk); > > + sk->removed = 1; > > + } > > + > > + pf_state_key_unref(sk); > > I think pf_state_key_unref() above needs to happen if and only if > sk != cur.
ah, yes. lemme think about this a sec. > > otherwise the diff looks good to me. > > thanks and > regards > sashan