netfilter: ipv6: nf_defrag: fix leakage of unqueued fragments
authorGuillaume Nault <gnault@redhat.com>
Sun, 2 Jun 2019 13:13:47 +0000 (15:13 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 21 Jul 2019 07:04:20 +0000 (09:04 +0200)
[ Upstream commit a0d56cb911ca301de81735f1d73c2aab424654ba ]

With commit 997dd9647164 ("net: IP6 defrag: use rbtrees in
nf_conntrack_reasm.c"), nf_ct_frag6_reasm() is now called from
nf_ct_frag6_queue(). With this change, nf_ct_frag6_queue() can fail
after the skb has been added to the fragment queue and
nf_ct_frag6_gather() was adapted to handle this case.

But nf_ct_frag6_queue() can still fail before the fragment has been
queued. nf_ct_frag6_gather() can't handle this case anymore, because it
has no way to know if nf_ct_frag6_queue() queued the fragment before
failing. If it didn't, the skb is lost as the error code is overwritten
with -EINPROGRESS.

Fix this by setting -EINPROGRESS directly in nf_ct_frag6_queue(), so
that nf_ct_frag6_gather() can propagate the error as is.

Fixes: 997dd9647164 ("net: IP6 defrag: use rbtrees in nf_conntrack_reasm.c")
Signed-off-by: Guillaume Nault <gnault@redhat.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/ipv6/netfilter/nf_conntrack_reasm.c

index cb1b4772dac0a8bfd4013491aee00d858f2acb54..73c29ddcfb95636beb62a73f28650f8087eddd19 100644 (file)
@@ -293,7 +293,11 @@ static int nf_ct_frag6_queue(struct frag_queue *fq, struct sk_buff *skb,
                skb->_skb_refdst = 0UL;
                err = nf_ct_frag6_reasm(fq, skb, prev, dev);
                skb->_skb_refdst = orefdst;
-               return err;
+
+               /* After queue has assumed skb ownership, only 0 or
+                * -EINPROGRESS must be returned.
+                */
+               return err ? -EINPROGRESS : 0;
        }
 
        skb_dst_drop(skb);
@@ -481,12 +485,6 @@ int nf_ct_frag6_gather(struct net *net, struct sk_buff *skb, u32 user)
                ret = 0;
        }
 
-       /* after queue has assumed skb ownership, only 0 or -EINPROGRESS
-        * must be returned.
-        */
-       if (ret)
-               ret = -EINPROGRESS;
-
        spin_unlock_bh(&fq->q.lock);
        inet_frag_put(&fq->q);
        return ret;