netfilter: x_tables: fix percpu counter block leak on error path when creating new...
authorPavel Tikhomirov <ptikhomirov@virtuozzo.com>
Mon, 13 Feb 2023 04:25:05 +0000 (12:25 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 11 Mar 2023 12:57:28 +0000 (13:57 +0100)
[ Upstream commit 0af8c09c896810879387decfba8c942994bb61f5 ]

Here is the stack where we allocate percpu counter block:

  +-< __alloc_percpu
    +-< xt_percpu_counter_alloc
      +-< find_check_entry # {arp,ip,ip6}_tables.c
        +-< translate_table

And it can be leaked on this code path:

  +-> ip6t_register_table
    +-> translate_table # allocates percpu counter block
    +-> xt_register_table # fails

there is no freeing of the counter block on xt_register_table fail.
Note: xt_percpu_counter_free should be called to free it like we do in
do_replace through cleanup_entry helper (or in __ip6t_unregister_table).

Probability of hitting this error path is low AFAICS (xt_register_table
can only return ENOMEM here, as it is not replacing anything, as we are
creating new netns, and it is hard to imagine that all previous
allocations succeeded and after that one in xt_register_table failed).
But it's worth fixing even the rare leak.

Fixes: 71ae0dff02d7 ("netfilter: xtables: use percpu rule counters")
Signed-off-by: Pavel Tikhomirov <ptikhomirov@virtuozzo.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/ipv4/netfilter/arp_tables.c
net/ipv4/netfilter/ip_tables.c
net/ipv6/netfilter/ip6_tables.c

index c53f14b943560766d38dfffbd7b5d529e95902fd..71bf3aeed73c103767b0281c5faf84a93f5dcd46 100644 (file)
@@ -1524,6 +1524,10 @@ int arpt_register_table(struct net *net,
 
        new_table = xt_register_table(net, table, &bootstrap, newinfo);
        if (IS_ERR(new_table)) {
+               struct arpt_entry *iter;
+
+               xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
+                       cleanup_entry(iter, net);
                xt_free_table_info(newinfo);
                return PTR_ERR(new_table);
        }
index 91301dc3924a2c7948bb1e0a288e771c94e50261..a748a1e7546050ee7fb77867ed9a1464438fe4f0 100644 (file)
@@ -1740,6 +1740,10 @@ int ipt_register_table(struct net *net, const struct xt_table *table,
 
        new_table = xt_register_table(net, table, &bootstrap, newinfo);
        if (IS_ERR(new_table)) {
+               struct ipt_entry *iter;
+
+               xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
+                       cleanup_entry(iter, net);
                xt_free_table_info(newinfo);
                return PTR_ERR(new_table);
        }
index 7ba68388d2e1f5a7c83f850ed671be3355196770..277a5ee887eb37feddd5f797165d21bc82e2d02c 100644 (file)
@@ -1750,6 +1750,10 @@ int ip6t_register_table(struct net *net, const struct xt_table *table,
 
        new_table = xt_register_table(net, table, &bootstrap, newinfo);
        if (IS_ERR(new_table)) {
+               struct ip6t_entry *iter;
+
+               xt_entry_foreach(iter, loc_cpu_entry, newinfo->size)
+                       cleanup_entry(iter, net);
                xt_free_table_info(newinfo);
                return PTR_ERR(new_table);
        }