netfilter: nf_tables: fix spurious set element insertion failure
[platform/kernel/linux-starfive.git] / net / netfilter / nf_tables_api.c
index dc56759..79c7eee 100644 (file)
@@ -151,6 +151,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
                return NULL;
 
        INIT_LIST_HEAD(&trans->list);
+       INIT_LIST_HEAD(&trans->binding_list);
        trans->msg_type = msg_type;
        trans->ctx      = *ctx;
 
@@ -163,13 +164,20 @@ static struct nft_trans *nft_trans_alloc(const struct nft_ctx *ctx,
        return nft_trans_alloc_gfp(ctx, msg_type, size, GFP_KERNEL);
 }
 
-static void nft_trans_destroy(struct nft_trans *trans)
+static void nft_trans_list_del(struct nft_trans *trans)
 {
        list_del(&trans->list);
+       list_del(&trans->binding_list);
+}
+
+static void nft_trans_destroy(struct nft_trans *trans)
+{
+       nft_trans_list_del(trans);
        kfree(trans);
 }
 
-static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
+static void __nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set,
+                                bool bind)
 {
        struct nftables_pernet *nft_net;
        struct net *net = ctx->net;
@@ -183,16 +191,82 @@ static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
                switch (trans->msg_type) {
                case NFT_MSG_NEWSET:
                        if (nft_trans_set(trans) == set)
-                               nft_trans_set_bound(trans) = true;
+                               nft_trans_set_bound(trans) = bind;
                        break;
                case NFT_MSG_NEWSETELEM:
                        if (nft_trans_elem_set(trans) == set)
-                               nft_trans_elem_set_bound(trans) = true;
+                               nft_trans_elem_set_bound(trans) = bind;
                        break;
                }
        }
 }
 
+static void nft_set_trans_bind(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       return __nft_set_trans_bind(ctx, set, true);
+}
+
+static void nft_set_trans_unbind(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       return __nft_set_trans_bind(ctx, set, false);
+}
+
+static void __nft_chain_trans_bind(const struct nft_ctx *ctx,
+                                  struct nft_chain *chain, bool bind)
+{
+       struct nftables_pernet *nft_net;
+       struct net *net = ctx->net;
+       struct nft_trans *trans;
+
+       if (!nft_chain_binding(chain))
+               return;
+
+       nft_net = nft_pernet(net);
+       list_for_each_entry_reverse(trans, &nft_net->commit_list, list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWCHAIN:
+                       if (nft_trans_chain(trans) == chain)
+                               nft_trans_chain_bound(trans) = bind;
+                       break;
+               case NFT_MSG_NEWRULE:
+                       if (trans->ctx.chain == chain)
+                               nft_trans_rule_bound(trans) = bind;
+                       break;
+               }
+       }
+}
+
+static void nft_chain_trans_bind(const struct nft_ctx *ctx,
+                                struct nft_chain *chain)
+{
+       __nft_chain_trans_bind(ctx, chain, true);
+}
+
+int nf_tables_bind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
+{
+       if (!nft_chain_binding(chain))
+               return 0;
+
+       if (nft_chain_binding(ctx->chain))
+               return -EOPNOTSUPP;
+
+       if (chain->bound)
+               return -EBUSY;
+
+       if (!nft_use_inc(&chain->use))
+               return -EMFILE;
+
+       chain->bound = true;
+       nft_chain_trans_bind(ctx, chain);
+
+       return 0;
+}
+
+void nf_tables_unbind_chain(const struct nft_ctx *ctx, struct nft_chain *chain)
+{
+       __nft_chain_trans_bind(ctx, chain, false);
+}
+
 static int nft_netdev_register_hooks(struct net *net,
                                     struct list_head *hook_list)
 {
@@ -292,6 +366,19 @@ static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *tr
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
 
+       switch (trans->msg_type) {
+       case NFT_MSG_NEWSET:
+               if (!nft_trans_set_update(trans) &&
+                   nft_set_is_anonymous(nft_trans_set(trans)))
+                       list_add_tail(&trans->binding_list, &nft_net->binding_list);
+               break;
+       case NFT_MSG_NEWCHAIN:
+               if (!nft_trans_chain_update(trans) &&
+                   nft_chain_binding(nft_trans_chain(trans)))
+                       list_add_tail(&trans->binding_list, &nft_net->binding_list);
+               break;
+       }
+
        list_add_tail(&trans->list, &nft_net->commit_list);
 }
 
@@ -338,8 +425,9 @@ static struct nft_trans *nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
                                ntohl(nla_get_be32(ctx->nla[NFTA_CHAIN_ID]));
                }
        }
-
+       nft_trans_chain(trans) = ctx->chain;
        nft_trans_commit_list_add_tail(ctx->net, trans);
+
        return trans;
 }
 
@@ -351,14 +439,13 @@ static int nft_delchain(struct nft_ctx *ctx)
        if (IS_ERR(trans))
                return PTR_ERR(trans);
 
-       ctx->table->use--;
+       nft_use_dec(&ctx->table->use);
        nft_deactivate_next(ctx->net, ctx->chain);
 
        return 0;
 }
 
-static void nft_rule_expr_activate(const struct nft_ctx *ctx,
-                                  struct nft_rule *rule)
+void nft_rule_expr_activate(const struct nft_ctx *ctx, struct nft_rule *rule)
 {
        struct nft_expr *expr;
 
@@ -371,9 +458,8 @@ static void nft_rule_expr_activate(const struct nft_ctx *ctx,
        }
 }
 
-static void nft_rule_expr_deactivate(const struct nft_ctx *ctx,
-                                    struct nft_rule *rule,
-                                    enum nft_trans_phase phase)
+void nft_rule_expr_deactivate(const struct nft_ctx *ctx, struct nft_rule *rule,
+                             enum nft_trans_phase phase)
 {
        struct nft_expr *expr;
 
@@ -392,7 +478,7 @@ nf_tables_delrule_deactivate(struct nft_ctx *ctx, struct nft_rule *rule)
        /* You cannot delete the same rule twice */
        if (nft_is_active_next(ctx->net, rule)) {
                nft_deactivate_next(ctx->net, rule);
-               ctx->chain->use--;
+               nft_use_dec(&ctx->chain->use);
                return 0;
        }
        return -ENOENT;
@@ -483,6 +569,7 @@ static int __nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
                nft_trans_set_update(trans) = true;
                nft_trans_set_gc_int(trans) = desc->gc_int;
                nft_trans_set_timeout(trans) = desc->timeout;
+               nft_trans_set_size(trans) = desc->size;
        }
        nft_trans_commit_list_add_tail(ctx->net, trans);
 
@@ -495,6 +582,58 @@ static int nft_trans_set_add(const struct nft_ctx *ctx, int msg_type,
        return __nft_trans_set_add(ctx, msg_type, set, NULL);
 }
 
+static void nft_setelem_data_deactivate(const struct net *net,
+                                       const struct nft_set *set,
+                                       struct nft_set_elem *elem);
+
+static int nft_mapelem_deactivate(const struct nft_ctx *ctx,
+                                 struct nft_set *set,
+                                 const struct nft_set_iter *iter,
+                                 struct nft_set_elem *elem)
+{
+       nft_setelem_data_deactivate(ctx->net, set, elem);
+
+       return 0;
+}
+
+struct nft_set_elem_catchall {
+       struct list_head        list;
+       struct rcu_head         rcu;
+       void                    *elem;
+};
+
+static void nft_map_catchall_deactivate(const struct nft_ctx *ctx,
+                                       struct nft_set *set)
+{
+       u8 genmask = nft_genmask_next(ctx->net);
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+
+       list_for_each_entry(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask))
+                       continue;
+
+               elem.priv = catchall->elem;
+               nft_setelem_data_deactivate(ctx->net, set, &elem);
+               break;
+       }
+}
+
+static void nft_map_deactivate(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       struct nft_set_iter iter = {
+               .genmask        = nft_genmask_next(ctx->net),
+               .fn             = nft_mapelem_deactivate,
+       };
+
+       set->ops->walk(ctx, set, &iter);
+       WARN_ON_ONCE(iter.err);
+
+       nft_map_catchall_deactivate(ctx, set);
+}
+
 static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
 {
        int err;
@@ -503,8 +642,11 @@ static int nft_delset(const struct nft_ctx *ctx, struct nft_set *set)
        if (err < 0)
                return err;
 
+       if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+               nft_map_deactivate(ctx, set);
+
        nft_deactivate_next(ctx->net, set);
-       ctx->table->use--;
+       nft_use_dec(&ctx->table->use);
 
        return err;
 }
@@ -536,7 +678,7 @@ static int nft_delobj(struct nft_ctx *ctx, struct nft_object *obj)
                return err;
 
        nft_deactivate_next(ctx->net, obj);
-       ctx->table->use--;
+       nft_use_dec(&ctx->table->use);
 
        return err;
 }
@@ -571,7 +713,7 @@ static int nft_delflowtable(struct nft_ctx *ctx,
                return err;
 
        nft_deactivate_next(ctx->net, flowtable);
-       ctx->table->use--;
+       nft_use_dec(&ctx->table->use);
 
        return err;
 }
@@ -1600,6 +1742,8 @@ static int nft_dump_basechain_hook(struct sk_buff *skb, int family,
 
        if (nft_base_chain_netdev(family, ops->hooknum)) {
                nest_devs = nla_nest_start_noflag(skb, NFTA_HOOK_DEVS);
+               if (!nest_devs)
+                       goto nla_put_failure;
 
                if (!hook_list)
                        hook_list = &basechain->hook_list;
@@ -2224,7 +2368,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
        return 0;
 }
 
-static int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
+int nft_chain_add(struct nft_table *table, struct nft_chain *chain)
 {
        int err;
 
@@ -2254,9 +2398,6 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
        struct nft_chain *chain;
        int err;
 
-       if (table->use == UINT_MAX)
-               return -EOVERFLOW;
-
        if (nla[NFTA_CHAIN_HOOK]) {
                struct nft_stats __percpu *stats = NULL;
                struct nft_chain_hook hook = {};
@@ -2352,6 +2493,11 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
        if (err < 0)
                goto err_destroy_chain;
 
+       if (!nft_use_inc(&table->use)) {
+               err = -EMFILE;
+               goto err_use;
+       }
+
        trans = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN);
        if (IS_ERR(trans)) {
                err = PTR_ERR(trans);
@@ -2368,10 +2514,11 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                goto err_unregister_hook;
        }
 
-       table->use++;
-
        return 0;
+
 err_unregister_hook:
+       nft_use_dec_restore(&table->use);
+err_use:
        nf_tables_unregister_hook(net, table, chain);
 err_destroy_chain:
        nf_tables_chain_destroy(ctx);
@@ -2526,6 +2673,8 @@ static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy,
        nft_trans_basechain(trans) = basechain;
        INIT_LIST_HEAD(&nft_trans_chain_hooks(trans));
        list_splice(&hook.list, &nft_trans_chain_hooks(trans));
+       if (nla[NFTA_CHAIN_HOOK])
+               module_put(hook.type->owner);
 
        nft_trans_commit_list_add_tail(ctx->net, trans);
 
@@ -2550,7 +2699,7 @@ err_hooks:
 
 static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
                                               const struct nft_table *table,
-                                              const struct nlattr *nla)
+                                              const struct nlattr *nla, u8 genmask)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
        u32 id = ntohl(nla_get_be32(nla));
@@ -2561,7 +2710,8 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
 
                if (trans->msg_type == NFT_MSG_NEWCHAIN &&
                    chain->table == table &&
-                   id == nft_trans_chain_id(trans))
+                   id == nft_trans_chain_id(trans) &&
+                   nft_active_genmask(chain, genmask))
                        return chain;
        }
        return ERR_PTR(-ENOENT);
@@ -2668,21 +2818,18 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
        return nf_tables_addchain(&ctx, family, genmask, policy, flags, extack);
 }
 
-static int nft_delchain_hook(struct nft_ctx *ctx, struct nft_chain *chain,
+static int nft_delchain_hook(struct nft_ctx *ctx,
+                            struct nft_base_chain *basechain,
                             struct netlink_ext_ack *extack)
 {
+       const struct nft_chain *chain = &basechain->chain;
        const struct nlattr * const *nla = ctx->nla;
        struct nft_chain_hook chain_hook = {};
-       struct nft_base_chain *basechain;
        struct nft_hook *this, *hook;
        LIST_HEAD(chain_del_list);
        struct nft_trans *trans;
        int err;
 
-       if (!nft_is_base_chain(chain))
-               return -EOPNOTSUPP;
-
-       basechain = nft_base_chain(chain);
        err = nft_chain_parse_hook(ctx->net, basechain, nla, &chain_hook,
                                   ctx->family, chain->flags, extack);
        if (err < 0)
@@ -2767,7 +2914,12 @@ static int nf_tables_delchain(struct sk_buff *skb, const struct nfnl_info *info,
                if (chain->flags & NFT_CHAIN_HW_OFFLOAD)
                        return -EOPNOTSUPP;
 
-               return nft_delchain_hook(&ctx, chain, extack);
+               if (nft_is_base_chain(chain)) {
+                       struct nft_base_chain *basechain = nft_base_chain(chain);
+
+                       if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
+                               return nft_delchain_hook(&ctx, basechain, extack);
+               }
        }
 
        if (info->nlh->nlmsg_flags & NLM_F_NONREC &&
@@ -3488,8 +3640,7 @@ err_fill_rule_info:
        return err;
 }
 
-static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
-                                  struct nft_rule *rule)
+void nf_tables_rule_destroy(const struct nft_ctx *ctx, struct nft_rule *rule)
 {
        struct nft_expr *expr, *next;
 
@@ -3506,7 +3657,7 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
        kfree(rule);
 }
 
-void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
+static void nf_tables_rule_release(const struct nft_ctx *ctx, struct nft_rule *rule)
 {
        nft_rule_expr_deactivate(ctx, rule, NFT_TRANS_RELEASE);
        nf_tables_rule_destroy(ctx, rule);
@@ -3594,12 +3745,6 @@ int nft_setelem_validate(const struct nft_ctx *ctx, struct nft_set *set,
        return 0;
 }
 
-struct nft_set_elem_catchall {
-       struct list_head        list;
-       struct rcu_head         rcu;
-       void                    *elem;
-};
-
 int nft_set_catchall_validate(const struct nft_ctx *ctx, struct nft_set *set)
 {
        u8 genmask = nft_genmask_next(ctx->net);
@@ -3670,7 +3815,8 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
                        return -EOPNOTSUPP;
 
        } else if (nla[NFTA_RULE_CHAIN_ID]) {
-               chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]);
+               chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID],
+                                             genmask);
                if (IS_ERR(chain)) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
                        return PTR_ERR(chain);
@@ -3701,9 +3847,6 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
                        return -EINVAL;
                handle = nf_tables_alloc_handle(table);
 
-               if (chain->use == UINT_MAX)
-                       return -EOVERFLOW;
-
                if (nla[NFTA_RULE_POSITION]) {
                        pos_handle = be64_to_cpu(nla_get_be64(nla[NFTA_RULE_POSITION]));
                        old_rule = __nft_rule_lookup(chain, pos_handle);
@@ -3797,6 +3940,11 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
                }
        }
 
+       if (!nft_use_inc(&chain->use)) {
+               err = -EMFILE;
+               goto err_release_rule;
+       }
+
        if (info->nlh->nlmsg_flags & NLM_F_REPLACE) {
                err = nft_delrule(&ctx, old_rule);
                if (err < 0)
@@ -3828,7 +3976,6 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
                }
        }
        kvfree(expr_info);
-       chain->use++;
 
        if (flow)
                nft_trans_flow_rule(trans) = flow;
@@ -3839,10 +3986,12 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
        return 0;
 
 err_destroy_flow_rule:
+       nft_use_dec_restore(&chain->use);
        if (flow)
                nft_flow_rule_destroy(flow);
 err_release_rule:
-       nf_tables_rule_release(&ctx, rule);
+       nft_rule_expr_deactivate(&ctx, rule, NFT_TRANS_PREPARE_ERROR);
+       nf_tables_rule_destroy(&ctx, rule);
 err_release_expr:
        for (i = 0; i < n; i++) {
                if (expr_info[i].ops) {
@@ -4774,6 +4923,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
                if (!(flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
 
+               if (flags & NFT_SET_ANONYMOUS)
+                       return -EOPNOTSUPP;
+
                err = nf_msecs_to_jiffies64(nla[NFTA_SET_TIMEOUT], &desc.timeout);
                if (err)
                        return err;
@@ -4782,6 +4934,10 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
        if (nla[NFTA_SET_GC_INTERVAL] != NULL) {
                if (!(flags & NFT_SET_TIMEOUT))
                        return -EINVAL;
+
+               if (flags & NFT_SET_ANONYMOUS)
+                       return -EOPNOTSUPP;
+
                desc.gc_int = ntohl(nla_get_be32(nla[NFTA_SET_GC_INTERVAL]));
        }
 
@@ -4828,6 +4984,9 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
                if (info->nlh->nlmsg_flags & NLM_F_REPLACE)
                        return -EOPNOTSUPP;
 
+               if (nft_set_is_anonymous(set))
+                       return -EOPNOTSUPP;
+
                err = nft_set_expr_alloc(&ctx, set, nla, exprs, &num_exprs, flags);
                if (err < 0)
                        return err;
@@ -4864,9 +5023,15 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
        alloc_size = sizeof(*set) + size + udlen;
        if (alloc_size < size || alloc_size > INT_MAX)
                return -ENOMEM;
+
+       if (!nft_use_inc(&table->use))
+               return -EMFILE;
+
        set = kvzalloc(alloc_size, GFP_KERNEL_ACCOUNT);
-       if (!set)
-               return -ENOMEM;
+       if (!set) {
+               err = -ENOMEM;
+               goto err_alloc;
+       }
 
        name = nla_strdup(nla[NFTA_SET_NAME], GFP_KERNEL_ACCOUNT);
        if (!name) {
@@ -4917,24 +5082,28 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
 
        set->num_exprs = num_exprs;
        set->handle = nf_tables_alloc_handle(table);
+       INIT_LIST_HEAD(&set->pending_update);
 
        err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
        if (err < 0)
                goto err_set_expr_alloc;
 
        list_add_tail_rcu(&set->list, &table->sets);
-       table->use++;
+
        return 0;
 
 err_set_expr_alloc:
        for (i = 0; i < set->num_exprs; i++)
                nft_expr_destroy(&ctx, set->exprs[i]);
 err_set_destroy:
-       ops->destroy(set);
+       ops->destroy(&ctx, set);
 err_set_init:
        kfree(set->name);
 err_set_name:
        kvfree(set);
+err_alloc:
+       nft_use_dec_restore(&table->use);
+
        return err;
 }
 
@@ -4945,7 +5114,7 @@ static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
 
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                list_del_rcu(&catchall->list);
-               nft_set_elem_destroy(set, catchall->elem, true);
+               nf_tables_set_elem_destroy(ctx, set, catchall->elem);
                kfree_rcu(catchall, rcu);
        }
 }
@@ -4960,7 +5129,7 @@ static void nft_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
        for (i = 0; i < set->num_exprs; i++)
                nft_expr_destroy(ctx, set->exprs[i]);
 
-       set->ops->destroy(set);
+       set->ops->destroy(ctx, set);
        nft_set_catchall_destroy(ctx, set);
        kfree(set->name);
        kvfree(set);
@@ -5073,9 +5242,6 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
        struct nft_set_binding *i;
        struct nft_set_iter iter;
 
-       if (set->use == UINT_MAX)
-               return -EOVERFLOW;
-
        if (!list_empty(&set->bindings) && nft_set_is_anonymous(set))
                return -EBUSY;
 
@@ -5103,10 +5269,12 @@ int nf_tables_bind_set(const struct nft_ctx *ctx, struct nft_set *set,
                        return iter.err;
        }
 bind:
+       if (!nft_use_inc(&set->use))
+               return -EMFILE;
+
        binding->chain = ctx->chain;
        list_add_tail_rcu(&binding->list, &set->bindings);
        nft_set_trans_bind(ctx, set);
-       set->use++;
 
        return 0;
 }
@@ -5125,12 +5293,62 @@ static void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
        }
 }
 
+static void nft_setelem_data_activate(const struct net *net,
+                                     const struct nft_set *set,
+                                     struct nft_set_elem *elem);
+
+static int nft_mapelem_activate(const struct nft_ctx *ctx,
+                               struct nft_set *set,
+                               const struct nft_set_iter *iter,
+                               struct nft_set_elem *elem)
+{
+       nft_setelem_data_activate(ctx->net, set, elem);
+
+       return 0;
+}
+
+static void nft_map_catchall_activate(const struct nft_ctx *ctx,
+                                     struct nft_set *set)
+{
+       u8 genmask = nft_genmask_next(ctx->net);
+       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+
+       list_for_each_entry(catchall, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+               if (!nft_set_elem_active(ext, genmask))
+                       continue;
+
+               elem.priv = catchall->elem;
+               nft_setelem_data_activate(ctx->net, set, &elem);
+               break;
+       }
+}
+
+static void nft_map_activate(const struct nft_ctx *ctx, struct nft_set *set)
+{
+       struct nft_set_iter iter = {
+               .genmask        = nft_genmask_next(ctx->net),
+               .fn             = nft_mapelem_activate,
+       };
+
+       set->ops->walk(ctx, set, &iter);
+       WARN_ON_ONCE(iter.err);
+
+       nft_map_catchall_activate(ctx, set);
+}
+
 void nf_tables_activate_set(const struct nft_ctx *ctx, struct nft_set *set)
 {
-       if (nft_set_is_anonymous(set))
+       if (nft_set_is_anonymous(set)) {
+               if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                       nft_map_activate(ctx, set);
+
                nft_clear(ctx->net, set);
+       }
 
-       set->use++;
+       nft_use_inc_restore(&set->use);
 }
 EXPORT_SYMBOL_GPL(nf_tables_activate_set);
 
@@ -5139,15 +5357,31 @@ void nf_tables_deactivate_set(const struct nft_ctx *ctx, struct nft_set *set,
                              enum nft_trans_phase phase)
 {
        switch (phase) {
-       case NFT_TRANS_PREPARE:
+       case NFT_TRANS_PREPARE_ERROR:
+               nft_set_trans_unbind(ctx, set);
                if (nft_set_is_anonymous(set))
                        nft_deactivate_next(ctx->net, set);
+               else
+                       list_del_rcu(&binding->list);
+
+               nft_use_dec(&set->use);
+               break;
+       case NFT_TRANS_PREPARE:
+               if (nft_set_is_anonymous(set)) {
+                       if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                               nft_map_deactivate(ctx, set);
 
-               set->use--;
+                       nft_deactivate_next(ctx->net, set);
+               }
+               nft_use_dec(&set->use);
                return;
        case NFT_TRANS_ABORT:
        case NFT_TRANS_RELEASE:
-               set->use--;
+               if (nft_set_is_anonymous(set) &&
+                   set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                       nft_map_deactivate(ctx, set);
+
+               nft_use_dec(&set->use);
                fallthrough;
        default:
                nf_tables_unbind_set(ctx, set, binding,
@@ -5228,7 +5462,8 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
 
 static int nft_set_elem_expr_dump(struct sk_buff *skb,
                                  const struct nft_set *set,
-                                 const struct nft_set_ext *ext)
+                                 const struct nft_set_ext *ext,
+                                 bool reset)
 {
        struct nft_set_elem_expr *elem_expr;
        u32 size, num_exprs = 0;
@@ -5241,7 +5476,7 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb,
 
        if (num_exprs == 1) {
                expr = nft_setelem_expr_at(elem_expr, 0);
-               if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr, false) < 0)
+               if (nft_expr_dump(skb, NFTA_SET_ELEM_EXPR, expr, reset) < 0)
                        return -1;
 
                return 0;
@@ -5252,7 +5487,7 @@ static int nft_set_elem_expr_dump(struct sk_buff *skb,
 
                nft_setelem_expr_foreach(expr, elem_expr, size) {
                        expr = nft_setelem_expr_at(elem_expr, size);
-                       if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, false) < 0)
+                       if (nft_expr_dump(skb, NFTA_LIST_ELEM, expr, reset) < 0)
                                goto nla_put_failure;
                }
                nla_nest_end(skb, nest);
@@ -5265,11 +5500,13 @@ nla_put_failure:
 
 static int nf_tables_fill_setelem(struct sk_buff *skb,
                                  const struct nft_set *set,
-                                 const struct nft_set_elem *elem)
+                                 const struct nft_set_elem *elem,
+                                 bool reset)
 {
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
+       u64 timeout = 0;
 
        nest = nla_nest_start_noflag(skb, NFTA_LIST_ELEM);
        if (nest == NULL)
@@ -5292,7 +5529,7 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                goto nla_put_failure;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPRESSIONS) &&
-           nft_set_elem_expr_dump(skb, set, ext))
+           nft_set_elem_expr_dump(skb, set, ext, reset))
                goto nla_put_failure;
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF) &&
@@ -5305,11 +5542,15 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                         htonl(*nft_set_ext_flags(ext))))
                goto nla_put_failure;
 
-       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT) &&
-           nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
-                        nf_jiffies64_to_msecs(*nft_set_ext_timeout(ext)),
-                        NFTA_SET_ELEM_PAD))
-               goto nla_put_failure;
+       if (nft_set_ext_exists(ext, NFT_SET_EXT_TIMEOUT)) {
+               timeout = *nft_set_ext_timeout(ext);
+               if (nla_put_be64(skb, NFTA_SET_ELEM_TIMEOUT,
+                                nf_jiffies64_to_msecs(timeout),
+                                NFTA_SET_ELEM_PAD))
+                       goto nla_put_failure;
+       } else if (set->flags & NFT_SET_TIMEOUT) {
+               timeout = READ_ONCE(set->timeout);
+       }
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_EXPIRATION)) {
                u64 expires, now = get_jiffies_64();
@@ -5324,6 +5565,9 @@ static int nf_tables_fill_setelem(struct sk_buff *skb,
                                 nf_jiffies64_to_msecs(expires),
                                 NFTA_SET_ELEM_PAD))
                        goto nla_put_failure;
+
+               if (reset)
+                       *nft_set_ext_expiration(ext) = now + timeout;
        }
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_USERDATA)) {
@@ -5347,6 +5591,7 @@ struct nft_set_dump_args {
        const struct netlink_callback   *cb;
        struct nft_set_iter             iter;
        struct sk_buff                  *skb;
+       bool                            reset;
 };
 
 static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
@@ -5357,7 +5602,7 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
        struct nft_set_dump_args *args;
 
        args = container_of(iter, struct nft_set_dump_args, iter);
-       return nf_tables_fill_setelem(args->skb, set, elem);
+       return nf_tables_fill_setelem(args->skb, set, elem, args->reset);
 }
 
 struct nft_set_dump_ctx {
@@ -5366,7 +5611,7 @@ struct nft_set_dump_ctx {
 };
 
 static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb,
-                                const struct nft_set *set)
+                                const struct nft_set *set, bool reset)
 {
        struct nft_set_elem_catchall *catchall;
        u8 genmask = nft_genmask_cur(net);
@@ -5381,7 +5626,7 @@ static int nft_set_catchall_dump(struct net *net, struct sk_buff *skb,
                        continue;
 
                elem.priv = catchall->elem;
-               ret = nf_tables_fill_setelem(skb, set, &elem);
+               ret = nf_tables_fill_setelem(skb, set, &elem, reset);
                break;
        }
 
@@ -5399,6 +5644,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        bool set_found = false;
        struct nlmsghdr *nlh;
        struct nlattr *nest;
+       bool reset = false;
        u32 portid, seq;
        int event;
 
@@ -5446,8 +5692,12 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        if (nest == NULL)
                goto nla_put_failure;
 
+       if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
+               reset = true;
+
        args.cb                 = cb;
        args.skb                = skb;
+       args.reset              = reset;
        args.iter.genmask       = nft_genmask_cur(net);
        args.iter.skip          = cb->args[0];
        args.iter.count         = 0;
@@ -5456,7 +5706,7 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
        set->ops->walk(&dump_ctx->ctx, set, &args.iter);
 
        if (!args.iter.err && args.iter.count == cb->args[0])
-               args.iter.err = nft_set_catchall_dump(net, skb, set);
+               args.iter.err = nft_set_catchall_dump(net, skb, set, reset);
        rcu_read_unlock();
 
        nla_nest_end(skb, nest);
@@ -5494,7 +5744,8 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
                                       const struct nft_ctx *ctx, u32 seq,
                                       u32 portid, int event, u16 flags,
                                       const struct nft_set *set,
-                                      const struct nft_set_elem *elem)
+                                      const struct nft_set_elem *elem,
+                                      bool reset)
 {
        struct nlmsghdr *nlh;
        struct nlattr *nest;
@@ -5515,7 +5766,7 @@ static int nf_tables_fill_setelem_info(struct sk_buff *skb,
        if (nest == NULL)
                goto nla_put_failure;
 
-       err = nf_tables_fill_setelem(skb, set, elem);
+       err = nf_tables_fill_setelem(skb, set, elem, reset);
        if (err < 0)
                goto nla_put_failure;
 
@@ -5621,7 +5872,7 @@ static int nft_setelem_get(struct nft_ctx *ctx, struct nft_set *set,
 }
 
 static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
-                           const struct nlattr *attr)
+                           const struct nlattr *attr, bool reset)
 {
        struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
        struct nft_set_elem elem;
@@ -5665,7 +5916,8 @@ static int nft_get_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                return err;
 
        err = nf_tables_fill_setelem_info(skb, ctx, ctx->seq, ctx->portid,
-                                         NFT_MSG_NEWSETELEM, 0, set, &elem);
+                                         NFT_MSG_NEWSETELEM, 0, set, &elem,
+                                         reset);
        if (err < 0)
                goto err_fill_setelem;
 
@@ -5689,6 +5941,7 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
        struct nft_set *set;
        struct nlattr *attr;
        struct nft_ctx ctx;
+       bool reset = false;
        int rem, err = 0;
 
        table = nft_table_lookup(net, nla[NFTA_SET_ELEM_LIST_TABLE], family,
@@ -5723,8 +5976,11 @@ static int nf_tables_getsetelem(struct sk_buff *skb,
        if (!nla[NFTA_SET_ELEM_LIST_ELEMENTS])
                return -EINVAL;
 
+       if (NFNL_MSG_TYPE(info->nlh->nlmsg_type) == NFT_MSG_GETSETELEM_RESET)
+               reset = true;
+
        nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
-               err = nft_get_set_elem(&ctx, set, attr);
+               err = nft_get_set_elem(&ctx, set, attr, reset);
                if (err < 0) {
                        NL_SET_BAD_ATTR(extack, attr);
                        break;
@@ -5757,7 +6013,7 @@ static void nf_tables_setelem_notify(const struct nft_ctx *ctx,
                flags |= ctx->flags & (NLM_F_CREATE | NLM_F_EXCL);
 
        err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
-                                         set, elem);
+                                         set, elem, false);
        if (err < 0) {
                kfree_skb(skb);
                goto err;
@@ -5899,6 +6155,7 @@ static void nft_set_elem_expr_destroy(const struct nft_ctx *ctx,
                __nft_set_elem_expr_destroy(ctx, expr);
 }
 
+/* Drop references and destroy. Called from gc, dynset and abort path. */
 void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                          bool destroy_expr)
 {
@@ -5915,16 +6172,16 @@ void nft_set_elem_destroy(const struct nft_set *set, void *elem,
                nft_set_elem_expr_destroy(&ctx, nft_set_ext_expr(ext));
 
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
-               (*nft_set_ext_obj(ext))->use--;
+               nft_use_dec(&(*nft_set_ext_obj(ext))->use);
        kfree(elem);
 }
 EXPORT_SYMBOL_GPL(nft_set_elem_destroy);
 
-/* Only called from commit path, nft_setelem_data_deactivate() already deals
- * with the refcounting from the preparation phase.
+/* Destroy element. References have been already dropped in the preparation
+ * path via nft_setelem_data_deactivate().
  */
-static void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
-                                      const struct nft_set *set, void *elem)
+void nf_tables_set_elem_destroy(const struct nft_ctx *ctx,
+                               const struct nft_set *set, void *elem)
 {
        struct nft_set_ext *ext = nft_set_elem_ext(set, elem);
 
@@ -6417,8 +6674,16 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                                     set->objtype, genmask);
                if (IS_ERR(obj)) {
                        err = PTR_ERR(obj);
+                       obj = NULL;
                        goto err_parse_key_end;
                }
+
+               if (!nft_use_inc(&obj->use)) {
+                       err = -EMFILE;
+                       obj = NULL;
+                       goto err_parse_key_end;
+               }
+
                err = nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
                if (err < 0)
                        goto err_parse_key_end;
@@ -6487,19 +6752,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (flags)
                *nft_set_ext_flags(ext) = flags;
 
+       if (obj)
+               *nft_set_ext_obj(ext) = obj;
+
        if (ulen > 0) {
                if (nft_set_ext_check(&tmpl, NFT_SET_EXT_USERDATA, ulen) < 0) {
                        err = -EINVAL;
-                       goto err_elem_userdata;
+                       goto err_elem_free;
                }
                udata = nft_set_ext_userdata(ext);
                udata->len = ulen - 1;
                nla_memcpy(&udata->data, nla[NFTA_SET_ELEM_USERDATA], ulen);
        }
-       if (obj) {
-               *nft_set_ext_obj(ext) = obj;
-               obj->use++;
-       }
        err = nft_set_elem_expr_setup(ctx, &tmpl, ext, expr_array, num_exprs);
        if (err < 0)
                goto err_elem_free;
@@ -6539,10 +6803,13 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                goto err_element_clash;
        }
 
-       if (!(flags & NFT_SET_ELEM_CATCHALL) && set->size &&
-           !atomic_add_unless(&set->nelems, 1, set->size + set->ndeact)) {
-               err = -ENFILE;
-               goto err_set_full;
+       if (!(flags & NFT_SET_ELEM_CATCHALL)) {
+               unsigned int max = set->size ? set->size + set->ndeact : UINT_MAX;
+
+               if (!atomic_add_unless(&set->nelems, 1, max)) {
+                       err = -ENFILE;
+                       goto err_set_full;
+               }
        }
 
        nft_trans_elem(trans) = elem;
@@ -6554,14 +6821,14 @@ err_set_full:
 err_element_clash:
        kfree(trans);
 err_elem_free:
-       if (obj)
-               obj->use--;
-err_elem_userdata:
        nf_tables_set_elem_destroy(ctx, set, elem.priv);
 err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_release(&elem.data.val, desc.type);
 err_parse_key_end:
+       if (obj)
+               nft_use_dec_restore(&obj->use);
+
        nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
 err_parse_key:
        nft_data_release(&elem.key.val, NFT_DATA_VALUE);
@@ -6601,7 +6868,8 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
        if (IS_ERR(set))
                return PTR_ERR(set);
 
-       if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
+       if (!list_empty(&set->bindings) &&
+           (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
                return -EBUSY;
 
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
@@ -6634,23 +6902,13 @@ static int nf_tables_newsetelem(struct sk_buff *skb,
 void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
 {
        struct nft_chain *chain;
-       struct nft_rule *rule;
 
        if (type == NFT_DATA_VERDICT) {
                switch (data->verdict.code) {
                case NFT_JUMP:
                case NFT_GOTO:
                        chain = data->verdict.chain;
-                       chain->use++;
-
-                       if (!nft_chain_is_bound(chain))
-                               break;
-
-                       chain->table->use++;
-                       list_for_each_entry(rule, &chain->rules, list)
-                               chain->use++;
-
-                       nft_chain_add(chain->table, chain);
+                       nft_use_inc_restore(&chain->use);
                        break;
                }
        }
@@ -6665,7 +6923,7 @@ static void nft_setelem_data_activate(const struct net *net,
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                nft_data_hold(nft_set_ext_data(ext), set->dtype);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
-               (*nft_set_ext_obj(ext))->use++;
+               nft_use_inc_restore(&(*nft_set_ext_obj(ext))->use);
 }
 
 static void nft_setelem_data_deactivate(const struct net *net,
@@ -6677,7 +6935,7 @@ static void nft_setelem_data_deactivate(const struct net *net,
        if (nft_set_ext_exists(ext, NFT_SET_EXT_DATA))
                nft_data_release(nft_set_ext_data(ext), set->dtype);
        if (nft_set_ext_exists(ext, NFT_SET_EXT_OBJREF))
-               (*nft_set_ext_obj(ext))->use--;
+               nft_use_dec(&(*nft_set_ext_obj(ext))->use);
 }
 
 static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
@@ -6885,7 +7143,9 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
        set = nft_set_lookup(table, nla[NFTA_SET_ELEM_LIST_SET], genmask);
        if (IS_ERR(set))
                return PTR_ERR(set);
-       if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
+
+       if (!list_empty(&set->bindings) &&
+           (set->flags & (NFT_SET_CONSTANT | NFT_SET_ANONYMOUS)))
                return -EBUSY;
 
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
@@ -7218,9 +7478,14 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
 
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
+       if (!nft_use_inc(&table->use))
+               return -EMFILE;
+
        type = nft_obj_type_get(net, objtype);
-       if (IS_ERR(type))
-               return PTR_ERR(type);
+       if (IS_ERR(type)) {
+               err = PTR_ERR(type);
+               goto err_type;
+       }
 
        obj = nft_obj_init(&ctx, type, nla[NFTA_OBJ_DATA]);
        if (IS_ERR(obj)) {
@@ -7254,7 +7519,7 @@ static int nf_tables_newobj(struct sk_buff *skb, const struct nfnl_info *info,
                goto err_obj_ht;
 
        list_add_tail_rcu(&obj->list, &table->objects);
-       table->use++;
+
        return 0;
 err_obj_ht:
        /* queued in transaction log */
@@ -7270,6 +7535,9 @@ err_strdup:
        kfree(obj);
 err_init:
        module_put(type->owner);
+err_type:
+       nft_use_dec_restore(&table->use);
+
        return err;
 }
 
@@ -7667,10 +7935,11 @@ void nf_tables_deactivate_flowtable(const struct nft_ctx *ctx,
                                    enum nft_trans_phase phase)
 {
        switch (phase) {
+       case NFT_TRANS_PREPARE_ERROR:
        case NFT_TRANS_PREPARE:
        case NFT_TRANS_ABORT:
        case NFT_TRANS_RELEASE:
-               flowtable->use--;
+               nft_use_dec(&flowtable->use);
                fallthrough;
        default:
                return;
@@ -8024,9 +8293,14 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
 
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, NULL, nla);
 
+       if (!nft_use_inc(&table->use))
+               return -EMFILE;
+
        flowtable = kzalloc(sizeof(*flowtable), GFP_KERNEL_ACCOUNT);
-       if (!flowtable)
-               return -ENOMEM;
+       if (!flowtable) {
+               err = -ENOMEM;
+               goto flowtable_alloc;
+       }
 
        flowtable->table = table;
        flowtable->handle = nf_tables_alloc_handle(table);
@@ -8081,7 +8355,6 @@ static int nf_tables_newflowtable(struct sk_buff *skb,
                goto err5;
 
        list_add_tail_rcu(&flowtable->list, &table->flowtables);
-       table->use++;
 
        return 0;
 err5:
@@ -8098,6 +8371,9 @@ err2:
        kfree(flowtable->name);
 err1:
        kfree(flowtable);
+flowtable_alloc:
+       nft_use_dec_restore(&table->use);
+
        return err;
 }
 
@@ -8711,6 +8987,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
                .attr_count     = NFTA_SET_ELEM_LIST_MAX,
                .policy         = nft_set_elem_list_policy,
        },
+       [NFT_MSG_GETSETELEM_RESET] = {
+               .call           = nf_tables_getsetelem,
+               .type           = NFNL_CB_RCU,
+               .attr_count     = NFTA_SET_ELEM_LIST_MAX,
+               .policy         = nft_set_elem_list_policy,
+       },
        [NFT_MSG_DELSETELEM] = {
                .call           = nf_tables_delsetelem,
                .type           = NFNL_CB_BATCH,
@@ -8939,7 +9221,7 @@ static void nf_tables_trans_destroy_work(struct work_struct *w)
        synchronize_rcu();
 
        list_for_each_entry_safe(trans, next, &head, list) {
-               list_del(&trans->list);
+               nft_trans_list_del(trans);
                nft_commit_release(trans);
        }
 }
@@ -9005,7 +9287,7 @@ static int nf_tables_commit_chain_prepare(struct net *net, struct nft_chain *cha
                                continue;
                        }
 
-                       if (WARN_ON_ONCE(data + expr->ops->size > data_boundary))
+                       if (WARN_ON_ONCE(data + size + expr->ops->size > data_boundary))
                                return -ENOMEM;
 
                        memcpy(data + size, expr, expr->ops->size);
@@ -9273,10 +9555,25 @@ static void nf_tables_commit_audit_log(struct list_head *adl, u32 generation)
        }
 }
 
+static void nft_set_commit_update(struct list_head *set_update_list)
+{
+       struct nft_set *set, *next;
+
+       list_for_each_entry_safe(set, next, set_update_list, pending_update) {
+               list_del_init(&set->pending_update);
+
+               if (!set->ops->commit)
+                       continue;
+
+               set->ops->commit(set);
+       }
+}
+
 static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
+       LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
        struct nft_chain *chain;
        struct nft_table *table;
@@ -9289,6 +9586,27 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                return 0;
        }
 
+       list_for_each_entry(trans, &nft_net->binding_list, binding_list) {
+               switch (trans->msg_type) {
+               case NFT_MSG_NEWSET:
+                       if (!nft_trans_set_update(trans) &&
+                           nft_set_is_anonymous(nft_trans_set(trans)) &&
+                           !nft_trans_set_bound(trans)) {
+                               pr_warn_once("nftables ruleset with unbound set\n");
+                               return -EINVAL;
+                       }
+                       break;
+               case NFT_MSG_NEWCHAIN:
+                       if (!nft_trans_chain_update(trans) &&
+                           nft_chain_binding(nft_trans_chain(trans)) &&
+                           !nft_trans_chain_bound(trans)) {
+                               pr_warn_once("nftables ruleset with unbound chain\n");
+                               return -EINVAL;
+                       }
+                       break;
+               }
+       }
+
        /* 0. Validate ruleset, otherwise roll back for error reporting. */
        if (nf_tables_validate(net) < 0)
                return -EAGAIN;
@@ -9425,6 +9743,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
 
                                WRITE_ONCE(set->timeout, nft_trans_set_timeout(trans));
                                WRITE_ONCE(set->gc_int, nft_trans_set_gc_int(trans));
+
+                               if (nft_trans_set_size(trans))
+                                       WRITE_ONCE(set->size, nft_trans_set_size(trans));
                        } else {
                                nft_clear(net, nft_trans_set(trans));
                                /* This avoids hitting -EBUSY when deleting the table
@@ -9432,7 +9753,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                                 */
                                if (nft_set_is_anonymous(nft_trans_set(trans)) &&
                                    !list_empty(&nft_trans_set(trans)->bindings))
-                                       trans->ctx.table->use--;
+                                       nft_use_dec(&trans->ctx.table->use);
                        }
                        nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
                                             NFT_MSG_NEWSET, GFP_KERNEL);
@@ -9451,6 +9772,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_setelem_notify(&trans->ctx, te->set,
                                                 &te->elem,
                                                 NFT_MSG_NEWSETELEM);
+                       if (te->set->ops->commit &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELSETELEM:
@@ -9465,6 +9791,11 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                                atomic_dec(&te->set->nelems);
                                te->set->ndeact--;
                        }
+                       if (te->set->ops->commit &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        break;
                case NFT_MSG_NEWOBJ:
                        if (nft_trans_obj_update(trans)) {
@@ -9527,6 +9858,8 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                }
        }
 
+       nft_set_commit_update(&set_update_list);
+
        nft_commit_notify(net, NETLINK_CB(skb).portid);
        nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
        nf_tables_commit_audit_log(&adl, nft_net->base_seq);
@@ -9586,10 +9919,25 @@ static void nf_tables_abort_release(struct nft_trans *trans)
        kfree(trans);
 }
 
+static void nft_set_abort_update(struct list_head *set_update_list)
+{
+       struct nft_set *set, *next;
+
+       list_for_each_entry_safe(set, next, set_update_list, pending_update) {
+               list_del_init(&set->pending_update);
+
+               if (!set->ops->abort)
+                       continue;
+
+               set->ops->abort(set);
+       }
+}
+
 static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
        struct nft_trans *trans, *next;
+       LIST_HEAD(set_update_list);
        struct nft_trans_elem *te;
 
        if (action == NFNL_ABORT_VALIDATE &&
@@ -9631,11 +9979,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                kfree(nft_trans_chain_name(trans));
                                nft_trans_destroy(trans);
                        } else {
-                               if (nft_chain_is_bound(trans->ctx.chain)) {
+                               if (nft_trans_chain_bound(trans)) {
                                        nft_trans_destroy(trans);
                                        break;
                                }
-                               trans->ctx.table->use--;
+                               nft_use_dec_restore(&trans->ctx.table->use);
                                nft_chain_del(trans->ctx.chain);
                                nf_tables_unregister_hook(trans->ctx.net,
                                                          trans->ctx.table,
@@ -9648,13 +9996,17 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                list_splice(&nft_trans_chain_hooks(trans),
                                            &nft_trans_basechain(trans)->hook_list);
                        } else {
-                               trans->ctx.table->use++;
+                               nft_use_inc_restore(&trans->ctx.table->use);
                                nft_clear(trans->ctx.net, trans->ctx.chain);
                        }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWRULE:
-                       trans->ctx.chain->use--;
+                       if (nft_trans_rule_bound(trans)) {
+                               nft_trans_destroy(trans);
+                               break;
+                       }
+                       nft_use_dec_restore(&trans->ctx.chain->use);
                        list_del_rcu(&nft_trans_rule(trans)->list);
                        nft_rule_expr_deactivate(&trans->ctx,
                                                 nft_trans_rule(trans),
@@ -9664,7 +10016,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        break;
                case NFT_MSG_DELRULE:
                case NFT_MSG_DESTROYRULE:
-                       trans->ctx.chain->use++;
+                       nft_use_inc_restore(&trans->ctx.chain->use);
                        nft_clear(trans->ctx.net, nft_trans_rule(trans));
                        nft_rule_expr_activate(&trans->ctx, nft_trans_rule(trans));
                        if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
@@ -9677,7 +10029,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                nft_trans_destroy(trans);
                                break;
                        }
-                       trans->ctx.table->use--;
+                       nft_use_dec_restore(&trans->ctx.table->use);
                        if (nft_trans_set_bound(trans)) {
                                nft_trans_destroy(trans);
                                break;
@@ -9686,8 +10038,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        break;
                case NFT_MSG_DELSET:
                case NFT_MSG_DESTROYSET:
-                       trans->ctx.table->use++;
+                       nft_use_inc_restore(&trans->ctx.table->use);
                        nft_clear(trans->ctx.net, nft_trans_set(trans));
+                       if (nft_trans_set(trans)->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                               nft_map_activate(&trans->ctx, nft_trans_set(trans));
+
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWSETELEM:
@@ -9699,6 +10054,12 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        nft_setelem_remove(net, te->set, &te->elem);
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                atomic_dec(&te->set->nelems);
+
+                       if (te->set->ops->abort &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        break;
                case NFT_MSG_DELSETELEM:
                case NFT_MSG_DESTROYSETELEM:
@@ -9709,6 +10070,11 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        if (!nft_setelem_is_catchall(te->set, &te->elem))
                                te->set->ndeact--;
 
+                       if (te->set->ops->abort &&
+                           list_empty(&te->set->pending_update)) {
+                               list_add_tail(&te->set->pending_update,
+                                             &set_update_list);
+                       }
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWOBJ:
@@ -9716,13 +10082,13 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans));
                                nft_trans_destroy(trans);
                        } else {
-                               trans->ctx.table->use--;
+                               nft_use_dec_restore(&trans->ctx.table->use);
                                nft_obj_del(nft_trans_obj(trans));
                        }
                        break;
                case NFT_MSG_DELOBJ:
                case NFT_MSG_DESTROYOBJ:
-                       trans->ctx.table->use++;
+                       nft_use_inc_restore(&trans->ctx.table->use);
                        nft_clear(trans->ctx.net, nft_trans_obj(trans));
                        nft_trans_destroy(trans);
                        break;
@@ -9731,7 +10097,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                nft_unregister_flowtable_net_hooks(net,
                                                &nft_trans_flowtable_hooks(trans));
                        } else {
-                               trans->ctx.table->use--;
+                               nft_use_dec_restore(&trans->ctx.table->use);
                                list_del_rcu(&nft_trans_flowtable(trans)->list);
                                nft_unregister_flowtable_net_hooks(net,
                                                &nft_trans_flowtable(trans)->hook_list);
@@ -9743,7 +10109,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                list_splice(&nft_trans_flowtable_hooks(trans),
                                            &nft_trans_flowtable(trans)->hook_list);
                        } else {
-                               trans->ctx.table->use++;
+                               nft_use_inc_restore(&trans->ctx.table->use);
                                nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
                        }
                        nft_trans_destroy(trans);
@@ -9751,11 +10117,13 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                }
        }
 
+       nft_set_abort_update(&set_update_list);
+
        synchronize_rcu();
 
        list_for_each_entry_safe_reverse(trans, next,
                                         &nft_net->commit_list, list) {
-               list_del(&trans->list);
+               nft_trans_list_del(trans);
                nf_tables_abort_release(trans);
        }
 
@@ -10149,6 +10517,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
 
        if (!tb[NFTA_VERDICT_CODE])
                return -EINVAL;
+
+       /* zero padding hole for memcmp */
+       memset(data, 0, sizeof(*data));
        data->verdict.code = ntohl(nla_get_be32(tb[NFTA_VERDICT_CODE]));
 
        switch (data->verdict.code) {
@@ -10174,7 +10545,8 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
                                                 genmask);
                } else if (tb[NFTA_VERDICT_CHAIN_ID]) {
                        chain = nft_chain_lookup_byid(ctx->net, ctx->table,
-                                                     tb[NFTA_VERDICT_CHAIN_ID]);
+                                                     tb[NFTA_VERDICT_CHAIN_ID],
+                                                     genmask);
                        if (IS_ERR(chain))
                                return PTR_ERR(chain);
                } else {
@@ -10190,8 +10562,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
                if (desc->flags & NFT_DATA_DESC_SETELEM &&
                    chain->flags & NFT_CHAIN_BINDING)
                        return -EINVAL;
+               if (!nft_use_inc(&chain->use))
+                       return -EMFILE;
 
-               chain->use++;
                data->verdict.chain = chain;
                break;
        }
@@ -10204,22 +10577,12 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
 static void nft_verdict_uninit(const struct nft_data *data)
 {
        struct nft_chain *chain;
-       struct nft_rule *rule;
 
        switch (data->verdict.code) {
        case NFT_JUMP:
        case NFT_GOTO:
                chain = data->verdict.chain;
-               chain->use--;
-
-               if (!nft_chain_is_bound(chain))
-                       break;
-
-               chain->table->use--;
-               list_for_each_entry(rule, &chain->rules, list)
-                       chain->use--;
-
-               nft_chain_del(chain);
+               nft_use_dec(&chain->use);
                break;
        }
 }
@@ -10388,11 +10751,11 @@ int __nft_release_basechain(struct nft_ctx *ctx)
        nf_tables_unregister_hook(ctx->net, ctx->chain->table, ctx->chain);
        list_for_each_entry_safe(rule, nr, &ctx->chain->rules, list) {
                list_del(&rule->list);
-               ctx->chain->use--;
+               nft_use_dec(&ctx->chain->use);
                nf_tables_rule_release(ctx, rule);
        }
        nft_chain_del(ctx->chain);
-       ctx->table->use--;
+       nft_use_dec(&ctx->table->use);
        nf_tables_chain_destroy(ctx);
 
        return 0;
@@ -10442,29 +10805,32 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
                ctx.chain = chain;
                list_for_each_entry_safe(rule, nr, &chain->rules, list) {
                        list_del(&rule->list);
-                       chain->use--;
+                       nft_use_dec(&chain->use);
                        nf_tables_rule_release(&ctx, rule);
                }
        }
        list_for_each_entry_safe(flowtable, nf, &table->flowtables, list) {
                list_del(&flowtable->list);
-               table->use--;
+               nft_use_dec(&table->use);
                nf_tables_flowtable_destroy(flowtable);
        }
        list_for_each_entry_safe(set, ns, &table->sets, list) {
                list_del(&set->list);
-               table->use--;
+               nft_use_dec(&table->use);
+               if (set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                       nft_map_deactivate(&ctx, set);
+
                nft_set_destroy(&ctx, set);
        }
        list_for_each_entry_safe(obj, ne, &table->objects, list) {
                nft_obj_del(obj);
-               table->use--;
+               nft_use_dec(&table->use);
                nft_obj_destroy(&ctx, obj);
        }
        list_for_each_entry_safe(chain, nc, &table->chains, list) {
                ctx.chain = chain;
                nft_chain_del(chain);
-               table->use--;
+               nft_use_dec(&table->use);
                nf_tables_chain_destroy(&ctx);
        }
        nf_tables_table_destroy(&ctx);
@@ -10538,6 +10904,7 @@ static int __net_init nf_tables_init_net(struct net *net)
 
        INIT_LIST_HEAD(&nft_net->tables);
        INIT_LIST_HEAD(&nft_net->commit_list);
+       INIT_LIST_HEAD(&nft_net->binding_list);
        INIT_LIST_HEAD(&nft_net->module_list);
        INIT_LIST_HEAD(&nft_net->notify_list);
        mutex_init(&nft_net->commit_mutex);