Hello,

On Wed, May 27, 2015 at 07:44:15PM +0200, Mike Belopuhov wrote:
> On Wed, May 27, 2015 at 10:39 +0200, Alexandr Nedvedicky wrote:
> > Hello,
> > 
> > > > -       if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) {
> > > > -               pf_state_key_detach(s, PF_SK_STACK);
> > > > -               pf_state_key_detach(s, PF_SK_WIRE);
> > > 
> > > This bug is not yours, but doing two pf_state_key_detach is wrong
> > > and results in all kinds of protection fault fireworks.  The right
> > > thing is to do pf_detach_state that would handle the case where
> > > PF_SK_STACK == PF_SK_WIRE (i.e. we need only one invocation).
> > good catch.
> > 
> > > 
> > > In csfailed case we do pf_remove_src_node and then call
> > > pf_src_tree_remove_state.  With your diff this means that
> > > pf_src_tree_remove_state will be dereferencing sni->sn that
> > > might be pool_put by the pf_remove_src_node.  Therefore their
> > > order needs to be reversed.
> > 
> > I see. Another option to fix it would be to do:
> > 
> >                     sni->sn = sns[i];
> >                     sns[i] = NULL;
> >
> 
> I'm not sure I follow.  Where do you want to do it?
> If it's when we pool_get sni's, then this would mean
> we won't run pf_remove_src_node and cleanup source
> nodes that might have been created.
> 

sorry I was too brief. The snippet below comes from
pf_create_state() with your patch applied:

   3560         for (i = 0; i < PF_SN_MAX; i++)
   3561                 if (sns[i] != NULL) {
   3562                         struct pf_sn_item       *sni;
   3563
   3564                         sni = pool_get(&pf_sn_item_pl, PR_NOWAIT);
   3565                         if (sni == NULL) {
   3566                                 REASON_SET(&reason, PFRES_MEMORY);
   3567                                 goto csfailed;
   3568                         }
   3569                         sni->sn = sns[i];
   3570                         sns[i] = NULL;
   3571                         SLIST_INSERT_HEAD(&s->src_nodes, sni, next);
   3572                         sni->sn->states++;
   3573                 }
   3574

the point of my suggestion is to transfer ownership of source node from
local variable sns[] to state. so the check performed later in csfailed
at line 3617, will find NULL pointer in sns[i] field and won't attempt
to call pf_remove_src_node().

   3610 csfailed:
   3611         if (s) {
   3612                 pf_normalize_tcp_cleanup(s);    /* safe even w/o init */
   3613                 pf_src_tree_remove_state(s);
   3614         }
   3615
   3616         for (i = 0; i < PF_SN_MAX; i++)
   3617                 if (sns[i] != NULL)
   3618                         pf_remove_src_node(sns[i]);
   3619


your patch updated by my suggestion is further below.

regards
sasha

? pf.c.diff
? pf.c.patch
Index: pf.c
===================================================================
RCS file: /cvs/src/sys/net/pf.c,v
retrieving revision 1.916
diff -u -r1.916 pf.c
--- pf.c        26 May 2015 16:17:51 -0000      1.916
+++ pf.c        27 May 2015 22:59:32 -0000
@@ -3557,16 +3557,6 @@
                goto csfailed;
        }
 
-       if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) {
-               pf_state_key_detach(s, PF_SK_STACK);
-               pf_state_key_detach(s, PF_SK_WIRE);
-               *sks = *skw = NULL;
-               REASON_SET(&reason, PFRES_STATEINS);
-               goto csfailed;
-       } else
-               *sm = s;
-
-       /* attach src nodes late, otherwise cleanup on error nontrivial */
        for (i = 0; i < PF_SN_MAX; i++)
                if (sns[i] != NULL) {
                        struct pf_sn_item       *sni;
@@ -3574,16 +3564,22 @@
                        sni = pool_get(&pf_sn_item_pl, PR_NOWAIT);
                        if (sni == NULL) {
                                REASON_SET(&reason, PFRES_MEMORY);
-                               pf_src_tree_remove_state(s);
-                               STATE_DEC_COUNTERS(s);
-                               pool_put(&pf_state_pl, s);
-                               return (PF_DROP);
+                               goto csfailed;
                        }
                        sni->sn = sns[i];
+                       sns[i] = NULL;
                        SLIST_INSERT_HEAD(&s->src_nodes, sni, next);
                        sni->sn->states++;
                }
 
+       if (pf_state_insert(BOUND_IFACE(r, pd->kif), skw, sks, s)) {
+               pf_detach_state(s);
+               *sks = *skw = NULL;
+               REASON_SET(&reason, PFRES_STATEINS);
+               goto csfailed;
+       } else
+               *sm = s;
+
        pf_set_rt_ifp(s, pd->src);      /* needs s->state_key set */
        if (tag > 0) {
                pf_tag_ref(tag);
@@ -3612,12 +3608,16 @@
        return (PF_PASS);
 
 csfailed:
+       if (s) {
+               pf_normalize_tcp_cleanup(s);    /* safe even w/o init */
+               pf_src_tree_remove_state(s);
+       }
+
        for (i = 0; i < PF_SN_MAX; i++)
                if (sns[i] != NULL)
                        pf_remove_src_node(sns[i]);
+
        if (s) {
-               pf_normalize_tcp_cleanup(s);    /* safe even w/o init */
-               pf_src_tree_remove_state(s);
                STATE_DEC_COUNTERS(s);
                pool_put(&pf_state_pl, s);
        }

Reply via email to