]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
netfilter: ipset: switch ext_size to atomic64_t
authorJozsef Kadlecsik <kadlec@netfilter.org>
Thu, 30 Jul 2026 18:38:50 +0000 (20:38 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Fri, 31 Jul 2026 13:57:38 +0000 (15:57 +0200)
The hash types do not acquire set->lock, they use 'region locking' where
only part of the hash table is locked. Parallel inserts and deletes are
possible and CPUs can race on ->ext_size update.  Switch to atomic64_t.

This leaves another bug unresolved: there still can be a race on
comment extension re-init.  This will be handled in a later commit
when converting to rhashtable backend.

Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports")
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/linux/netfilter/ipset/ip_set.h
net/netfilter/ipset/ip_set_bitmap_gen.h
net/netfilter/ipset/ip_set_core.c
net/netfilter/ipset/ip_set_hash_gen.h
net/netfilter/ipset/ip_set_list_set.c

index b98331572ad2989258e803fe111f7c900151003f..cadae9b2578f1f426762b4a6240cd5bffe849f4c 100644 (file)
@@ -273,7 +273,7 @@ struct ip_set {
        /* Number of elements (vs timeout) */
        u32 elements;
        /* Size of the dynamic extensions (vs timeout) */
-       size_t ext_size;
+       atomic64_t ext_size;
        /* Element data size */
        size_t dsize;
        /* Offsets to extensions in elements */
index bb9b5bed10e19c43c2be125791c0bcc2b7f06278..226fdf17b683292f7e177bc0aa82508c9dbaca07 100644 (file)
@@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set)
                mtype_ext_cleanup(set);
        bitmap_zero(map->members, map->elements);
        set->elements = 0;
-       set->ext_size = 0;
+       atomic64_set(&set->ext_size, 0);
 }
 
 /* Calculate the actual memory size of the set data */
@@ -93,7 +93,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct mtype *map = set->data;
        struct nlattr *nested;
-       size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size;
+       size_t memsize = mtype_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
 
        nested = nla_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)
index 6cfad152d7d1ba3ca872bb23e1764e3ff41103b7..822a53a7f502a9b92f9f4f8d9296e4989efd6474 100644 (file)
@@ -350,7 +350,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
        size_t len = ext->comment ? strlen(ext->comment) : 0;
 
        if (unlikely(c)) {
-               set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
+               atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
                rcu_assign_pointer(comment->c, NULL);
                kfree_rcu(c, rcu);
        }
@@ -362,7 +362,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment,
        if (unlikely(!c))
                return;
        strscpy(c->str, ext->comment, len + 1);
-       set->ext_size += sizeof(*c) + strlen(c->str) + 1;
+       atomic64_add(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
        rcu_assign_pointer(comment->c, c);
 }
 EXPORT_SYMBOL_GPL(ip_set_init_comment);
@@ -392,7 +392,7 @@ ip_set_comment_free(struct ip_set *set, void *ptr)
        c = rcu_dereference_protected(comment->c, 1);
        if (unlikely(!c))
                return;
-       set->ext_size -= sizeof(*c) + strlen(c->str) + 1;
+       atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size);
        rcu_assign_pointer(comment->c, NULL);
        kfree_rcu(c, rcu);
 }
index dd31992c915ca8be15b5c9d1c1f2cd27aeeb021f..8841daf28f017b80aca9ac79ae935394f89b772d 100644 (file)
@@ -1373,7 +1373,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb)
        rcu_read_lock_bh();
        t = rcu_dereference_bh(h->table);
        mtype_ext_size(set, &elements, &ext_size);
-       memsize = mtype_ahash_memsize(h, t) + ext_size + set->ext_size;
+       memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size);
        htable_bits = t->htable_bits;
        rcu_read_unlock_bh();
 
index 1cef84f15e8c75f51b59724314eb31afe3a6ac4f..ca3ef9479e838d80e714d0d46f1f73ac83c42462 100644 (file)
@@ -421,7 +421,7 @@ list_set_flush(struct ip_set *set)
        list_for_each_entry_safe(e, n, &map->members, list)
                list_set_del(set, e);
        set->elements = 0;
-       set->ext_size = 0;
+       atomic64_set(&set->ext_size, 0);
 }
 
 static void
@@ -455,7 +455,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb)
 {
        const struct list_set *map = set->data;
        struct nlattr *nested;
-       size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size;
+       size_t memsize = list_set_memsize(map, set->dsize) + atomic64_read(&set->ext_size);
 
        nested = nla_nest_start(skb, IPSET_ATTR_DATA);
        if (!nested)