When resizing is in progress, kernel side del element operations performed on the original set are saved in a list and replayed on the new set after resize finished. Make sure extensions are not freed when replaying the deletion of the given elements. Issue was discovered by sashiko. Signed-off-by: Jozsef Kadlecsik --- include/linux/netfilter/ipset/ip_set.h | 11 +++++++++-- net/netfilter/ipset/ip_set_hash_gen.h | 10 ++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index b98331572ad2..6d0d33680faa 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -113,6 +113,12 @@ struct ip_set_skbinfo { u16 __pad; }; +enum ip_set_ext_context { + IPSET_EXT_CONTEXT_NONE = 0, + IPSET_EXT_CONTEXT_TARGET = 1, + IPSET_EXT_CONTEXT_REPLAY = 2, +}; + struct ip_set_ext { struct ip_set_skbinfo skbinfo; u64 packets; @@ -121,7 +127,7 @@ struct ip_set_ext { u32 timeout; u8 packets_op; u8 bytes_op; - bool target; + u8 context; }; #define ext_timeout(e, s) \ @@ -530,7 +536,8 @@ nf_inet_addr_mask_inplace(union nf_inet_addr *a1, } #define IP_SET_INIT_KEXT(skb, opt, set) \ - { .bytes = (skb)->len, .packets = 1, .target = true,\ + { .bytes = (skb)->len, .packets = 1, \ + .context = IPSET_EXT_CONTEXT_TARGET, \ .timeout = ip_set_adt_opt_timeout(opt, set) } #define IP_SET_INIT_UEXT(set) \ diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index a41f6cdeed80..a95feb013ac5 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -630,6 +630,7 @@ mtype_resize(struct ip_set *set, bool retried) struct htable *t, *orig; u8 pos, htable_bits; size_t hsize, dsize = set->dsize; + struct ip_set_ext replay = { .context = IPSET_EXT_CONTEXT_REPLAY }; #ifdef IP_SET_HASH_WITH_NETS u8 flags; struct mtype_elem *tmp; @@ -779,7 +780,7 @@ mtype_resize(struct ip_set *set, bool retried) if (x->ad == IPSET_ADD) { mtype_add(set, &x->d, &x->ext, &x->mext, x->flags); } else { - mtype_del(set, &x->d, NULL, NULL, 0); + mtype_del(set, &x->d, &replay, &replay, 0); } list_del(l); kfree(l); @@ -1007,7 +1008,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, ret = 0; resize: spin_unlock_bh(&t->hregion[r].lock); - if (atomic_read(&t->ref) && ext->target) { + if (atomic_read(&t->ref) && ext->context == IPSET_EXT_CONTEXT_TARGET) { /* Resize is in process and kernel side add, save values */ struct mtype_resize_ad *x; @@ -1095,9 +1096,10 @@ 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); + if (ext->context != IPSET_EXT_CONTEXT_REPLAY) + ip_set_ext_destroy(set, data); - if (atomic_read(&t->ref) && ext->target) { + if (atomic_read(&t->ref) && ext->context == IPSET_EXT_CONTEXT_TARGET) { /* Resize is in process and kernel side del, * save values */ -- 2.39.5