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

Reply via email to