[PATCH RFC nf-next 06/12] netfilter: ipset: add and use ip_set_ext_destroy_slow

Florian Westphal <[email protected]> Tue, 14 Jul 2026 15:18:22 +0200
Newsgroups gmane.comp.security.firewalls.netfilter.devel
Message-ID <[email protected]>
same rationale as previous patch, however, this time we cannot
add lockdep assertion in ip_set_ext_destroy().

At this time the function has too many call sites where set->lock
is held (e.g. during set destruction).

Signed-off-by: Florian Westphal <[email protected]>
---
 include/linux/netfilter/ipset/ip_set.h  | 24 ++++++++++++++++++++----
 net/netfilter/ipset/ip_set_bitmap_gen.h |  2 ++
 net/netfilter/ipset/ip_set_hash_gen.h   |  8 ++++----
 3 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 50cd719bc270..f9003ec21259 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -282,17 +282,33 @@ struct ip_set {
 	void *data;
 };
 
+static inline void
+__ip_set_destroy_comment(struct ip_set *set, void *data)
+{
+	struct ip_set_comment *c = ext_comment(data, set);
+
+	ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(set, c);
+}
+
 static inline void
 ip_set_ext_destroy(struct ip_set *set, void *data)
 {
 	/* Check that the extension is enabled for the set and
 	 * call it's destroy function for its extension part in data.
 	 */
-	if (SET_WITH_COMMENT(set)) {
-		struct ip_set_comment *c = ext_comment(data, set);
+	if (SET_WITH_COMMENT(set))
+		__ip_set_destroy_comment(set, data);
+}
 
-		ip_set_extensions[IPSET_EXT_ID_COMMENT].destroy(set, c);
-	}
+static inline void
+ip_set_ext_destroy_slow(struct ip_set *set, void *data)
+{
+	if (!SET_WITH_COMMENT(set))
+		return;
+
+	spin_lock_bh(&set->lock);
+	__ip_set_destroy_comment(set, data);
+	spin_unlock_bh(&set->lock);
 }
 
 int ip_set_put_flags(struct sk_buff *skb, struct ip_set *set);
diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h
index b13cde902c17..ca68b6e51214 100644
--- a/net/netfilter/ipset/ip_set_bitmap_gen.h
+++ b/net/netfilter/ipset/ip_set_bitmap_gen.h
@@ -182,6 +182,8 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 	const struct mtype_adt_elem *e = value;
 	void *x = get_ext(set, map, e->id);
 
+	lockdep_assert_held(&set->lock);
+
 	if (mtype_do_del(e, map))
 		return -IPSET_ERR_EXIST;
 
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index c3dda56d786c..1eff2c065bb3 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -443,7 +443,7 @@ mtype_ext_cleanup(struct ip_set *set, struct hbucket *n)
 
 	for (i = 0; i < pos; i++)
 		if (test_bit(i, n->used))
-			ip_set_ext_destroy(set, ahash_data(n, i, set->dsize));
+			ip_set_ext_destroy_slow(set, ahash_data(n, i, set->dsize));
 }
 
 /* Flush a hash type of set: destroy all elements */
@@ -587,7 +587,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r)
 			smp_mb__after_atomic();
 			mtype_del_cidr_all(set, h, data);
 			t->hregion[r].elements--;
-			ip_set_ext_destroy(set, data);
+			ip_set_ext_destroy_slow(set, data);
 			d++;
 		}
 		if (d >= AHASH_INIT_SIZE) {
@@ -1012,7 +1012,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 		data = ahash_data(n, j, set->dsize);
 		if (!deleted) {
 			mtype_del_cidr_all(set, h, data);
-			ip_set_ext_destroy(set, data);
+			ip_set_ext_destroy_slow(set, data);
 			t->hregion[r].elements--;
 		}
 		goto copy_data;
@@ -1167,7 +1167,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext,
 			smp_store_release(&n->pos, --pos);
 		t->hregion[r].elements--;
 		mtype_del_cidr_all(set, h, d);
-		ip_set_ext_destroy(set, data);
+		ip_set_ext_destroy_slow(set, data);
 
 		if (t->resizing && ext && ext->target) {
 			/* Resize is in process and kernel side del,
-- 
2.54.0