netfilter: nf_tables: remove catchall element in GC sync path
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 6 Nov 2023 09:53:09 +0000 (10:53 +0100)
committerPablo Neira Ayuso <pablo@netfilter.org>
Wed, 8 Nov 2023 12:52:32 +0000 (13:52 +0100)
The expired catchall element is not deactivated and removed from GC sync
path. This path holds mutex so just call nft_setelem_data_deactivate()
and nft_setelem_catchall_remove() before queueing the GC work.

Fixes: 4a9e12ea7e70 ("netfilter: nft_set_pipapo: call nft_trans_gc_queue_sync() in catchall GC")
Reported-by: lonial con <kongln9170@gmail.com>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
net/netfilter/nf_tables_api.c

index 146b744..a761ee6 100644 (file)
@@ -6520,6 +6520,12 @@ static int nft_setelem_deactivate(const struct net *net,
        return ret;
 }
 
+static void nft_setelem_catchall_destroy(struct nft_set_elem_catchall *catchall)
+{
+       list_del_rcu(&catchall->list);
+       kfree_rcu(catchall, rcu);
+}
+
 static void nft_setelem_catchall_remove(const struct net *net,
                                        const struct nft_set *set,
                                        struct nft_elem_priv *elem_priv)
@@ -6528,8 +6534,7 @@ static void nft_setelem_catchall_remove(const struct net *net,
 
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                if (catchall->elem == elem_priv) {
-                       list_del_rcu(&catchall->list);
-                       kfree_rcu(catchall, rcu);
+                       nft_setelem_catchall_destroy(catchall);
                        break;
                }
        }
@@ -9678,11 +9683,12 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
                                                  unsigned int gc_seq,
                                                  bool sync)
 {
-       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem_catchall *catchall, *next;
        const struct nft_set *set = gc->set;
+       struct nft_elem_priv *elem_priv;
        struct nft_set_ext *ext;
 
-       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                ext = nft_set_elem_ext(set, catchall->elem);
 
                if (!nft_set_elem_expired(ext))
@@ -9700,7 +9706,13 @@ dead_elem:
                if (!gc)
                        return NULL;
 
-               nft_trans_gc_elem_add(gc, catchall->elem);
+               elem_priv = catchall->elem;
+               if (sync) {
+                       nft_setelem_data_deactivate(gc->net, gc->set, elem_priv);
+                       nft_setelem_catchall_destroy(catchall);
+               }
+
+               nft_trans_gc_elem_add(gc, elem_priv);
        }
 
        return gc;