[PATCH nf 1/1] netfilter: ipset: serialize kernel-side put-byindex with swap
Zhiling Zou <[email protected]> Sat, 1 Aug 2026 20:10:02 +0800
| Newsgroups | gmane.comp.security.firewalls.netfilter.devel,gmane.linux.network |
|---|---|
| Message-ID | <df40376603e2a53a311d5ceea63240bedc11bdfc.1785583673.git.zhilinz@nebusec.ai> |
list:set garbage collection drops member set references through
ip_set_put_byindex(), which resolves inst->ip_set_list[index] before
__ip_set_put() takes ip_set_ref_lock. ip_set_swap() swaps both the set
pointers and the corresponding refcounts while holding that same lock.
If garbage collection looks up the old set pointer for an index and then
loses the lock race to ip_set_swap(), it decrements the old object after
its refcount has already been swapped away. This can trigger BUG_ON(ref
== 0) when the other set had no references, or otherwise under-reference
a set that is still reachable through the swapped index.
Fix this by holding ip_set_ref_lock across the kernel-side lookup and
refcount decrement. This keeps ip_set_put_byindex() paired with the same
set/ref state that ip_set_swap() publishes, without changing normal
list:set deletion or swap behaviour.
Fixes: 439cd39ea136 ("netfilter: ipset: list:set: Decrease refcount synchronously on deletion and replace")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Signed-off-by: Zhiling Zou <[email protected]>
---
net/netfilter/ipset/ip_set_core.c | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 6cfad152d7d1b..2d3c8586e6445 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -855,9 +855,16 @@ __ip_set_put_byindex(struct ip_set_net *inst, ip_set_id_t index)
struct ip_set *set;
rcu_read_lock();
+ /* Serialize the list lookup with ip_set_swap(), which swaps both
+ * ip_set_list[] entries and set->ref under ip_set_ref_lock.
+ */
+ write_lock_bh(&ip_set_ref_lock);
set = rcu_dereference(inst->ip_set_list)[index];
- if (set)
- __ip_set_put(set);
+ if (set) {
+ BUG_ON(set->ref == 0);
+ set->ref--;
+ }
+ write_unlock_bh(&ip_set_ref_lock);
rcu_read_unlock();
}
--
2.43.0