]> git.itanic.dy.fi Git - linux-stable/commitdiff
netfilter: nf_tables: fix memleak when more than 255 elements expired
authorFlorian Westphal <fw@strlen.de>
Tue, 19 Sep 2023 13:36:13 +0000 (15:36 +0200)
committerFlorian Westphal <fw@strlen.de>
Wed, 20 Sep 2023 08:35:23 +0000 (10:35 +0200)
When more than 255 elements expired we're supposed to switch to a new gc
container structure.

This never happens: u8 type will wrap before reaching the boundary
and nft_trans_gc_space() always returns true.

This means we recycle the initial gc container structure and
lose track of the elements that came before.

While at it, don't deref 'gc' after we've passed it to call_rcu.

Fixes: 5f68718b34a5 ("netfilter: nf_tables: GC transaction API to avoid race with control plane")
Reported-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c

index a4455f4995abf8687aa391fd3dc609e1c048b6da..7c816359d5a98884cf303b87af1bb2adbb1b48e2 100644 (file)
@@ -1682,7 +1682,7 @@ struct nft_trans_gc {
        struct net              *net;
        struct nft_set          *set;
        u32                     seq;
-       u                     count;
+       u16                     count;
        void                    *priv[NFT_TRANS_GC_BATCHCOUNT];
        struct rcu_head         rcu;
 };
index a3680638ec60f7d1fd47daf44283e043b6cb6088..4356189360fb88d695b25070264783520aeef6b1 100644 (file)
@@ -9579,12 +9579,15 @@ static int nft_trans_gc_space(struct nft_trans_gc *trans)
 struct nft_trans_gc *nft_trans_gc_queue_async(struct nft_trans_gc *gc,
                                              unsigned int gc_seq, gfp_t gfp)
 {
+       struct nft_set *set;
+
        if (nft_trans_gc_space(gc))
                return gc;
 
+       set = gc->set;
        nft_trans_gc_queue_work(gc);
 
-       return nft_trans_gc_alloc(gc->set, gc_seq, gfp);
+       return nft_trans_gc_alloc(set, gc_seq, gfp);
 }
 
 void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans)
@@ -9599,15 +9602,18 @@ void nft_trans_gc_queue_async_done(struct nft_trans_gc *trans)
 
 struct nft_trans_gc *nft_trans_gc_queue_sync(struct nft_trans_gc *gc, gfp_t gfp)
 {
+       struct nft_set *set;
+
        if (WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net)))
                return NULL;
 
        if (nft_trans_gc_space(gc))
                return gc;
 
+       set = gc->set;
        call_rcu(&gc->rcu, nft_trans_gc_trans_free);
 
-       return nft_trans_gc_alloc(gc->set, 0, gfp);
+       return nft_trans_gc_alloc(set, 0, gfp);
 }
 
 void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)