During a hash set resize, mtype_resize() copies each element into the new table with memcpy(), so the new-table element shares the old-table element's comment extension. An xt_SET delete on the old table during the resize destroys that shared comment via ip_set_ext_destroy() and queues a replayed delete on h->ad. After the table swap mtype_resize() replays it with mtype_del() on the new table, whose copy still points at the freed comment, so ip_set_ext_destroy() frees it a second time: ODEBUG: activate active (active state 1) object: ... object type: rcu_head WARNING: CPU: 3 PID: 5311 at lib/debugobjects.c:514 debug_print_object Call Trace: kvfree_call_rcu (kernel/rcu/tree.c:3825) ip_set_comment_free (net/netfilter/ipset/ip_set_core.c:397) hash_ip4_del (net/netfilter/ipset/ip_set_hash_gen.h:1098) hash_ip4_kadt (net/netfilter/ipset/ip_set_hash_ip.c:96) ip_set_del (net/netfilter/ipset/ip_set_core.c:813) set_target_v3 (net/netfilter/xt_set.c:412) ipt_do_table (net/ipv4/netfilter/ip_tables.c:346) __ip_local_out (net/ipv4/ip_output.c:119) icmp_push_reply (net/ipv4/icmp.c:397) __icmp_send (net/ipv4/icmp.c:804) __udp4_lib_rcv (net/ipv4/udp.c:2521) ip_local_deliver (net/ipv4/ip_input.c:254) ip_rcv (net/ipv4/ip_input.c:569) The replay passes a NULL ext (the kernel-side delete that queued it already destroyed the extensions), so skip ip_set_ext_destroy() when ext is NULL. Reachable from an unprivileged user namespace. Fixes: f66ee0410b1c ("netfilter: ipset: Fix \"INFO: rcu detected stall in hash_xxx\" reports") Reported-by: Xiang Mei Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi --- v3: Repost as a new thread; v2 was sent in reply to v1. v2: Rebase onto nf-next; drop the second hunk (already fixed there). net/netfilter/ipset/ip_set_hash_gen.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8231317b0..d15530241 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1112,7 +1112,9 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, mtype_del_cidr(set, h, NCIDR_PUT(DCIDR_GET(d->cidr, j)), j); #endif - ip_set_ext_destroy(set, data); + /* On a resize replay the extensions were already destroyed. */ + if (ext) + ip_set_ext_destroy(set, data); if (t->resizing && ext && ext->target) { /* Resize is in process and kernel side del, -- 2.43.0