netfilter: nf_tables: disallow element updates of bound anonymous sets
[linux-block.git] / net / netfilter / nf_tables_api.c
index dc5675962de4fcfd90518b7ab3d145b60893e2d9..6f26520bd865065187191a5f82b6ffdc7fb10b7a 100644 (file)
@@ -169,7 +169,8 @@ static void nft_trans_destroy(struct nft_trans *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 +184,80 @@ 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;
+
+       chain->bound = true;
+       chain->use++;
+       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)
 {
@@ -338,8 +403,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;
 }
 
@@ -357,8 +423,7 @@ static int nft_delchain(struct nft_ctx *ctx)
        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 +436,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;
 
@@ -495,6 +559,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,6 +619,9 @@ 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--;
 
@@ -1600,6 +1719,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 +2345,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;
 
@@ -3488,8 +3609,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 +3626,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 +3714,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);
@@ -3842,7 +3956,8 @@ err_destroy_flow_rule:
        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) {
@@ -4917,6 +5032,7 @@ 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)
@@ -4930,7 +5046,7 @@ 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:
@@ -4945,7 +5061,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 +5076,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);
@@ -5125,10 +5241,60 @@ 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++;
 }
@@ -5139,14 +5305,28 @@ 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);
 
+               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);
+
+                       nft_deactivate_next(ctx->net, set);
+               }
                set->use--;
                return;
        case NFT_TRANS_ABORT:
        case NFT_TRANS_RELEASE:
+               if (nft_set_is_anonymous(set) &&
+                   set->flags & (NFT_SET_MAP | NFT_SET_OBJECT))
+                       nft_map_deactivate(ctx, set);
+
                set->use--;
                fallthrough;
        default:
@@ -5899,6 +6079,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)
 {
@@ -5920,11 +6101,11 @@ void nft_set_elem_destroy(const struct nft_set *set, void *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);
 
@@ -6487,19 +6668,19 @@ 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;
+               obj->use++;
+       }
        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;
@@ -6554,10 +6735,7 @@ 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);
+       nft_set_elem_destroy(set, elem.priv, true);
 err_parse_data:
        if (nla[NFTA_SET_ELEM_DATA] != NULL)
                nft_data_release(&elem.data.val, desc.type);
@@ -6601,7 +6779,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,7 +6813,6 @@ 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) {
@@ -6642,15 +6820,6 @@ void nft_data_hold(const struct nft_data *data, enum nft_data_types type)
                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);
                        break;
                }
        }
@@ -6885,7 +7054,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);
@@ -7667,6 +7838,7 @@ 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:
@@ -9005,7 +9177,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 +9445,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;
@@ -9451,6 +9638,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 +9657,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 +9724,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 +9785,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,7 +9845,7 @@ 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;
                                }
@@ -9654,6 +9868,10 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_NEWRULE:
+                       if (nft_trans_rule_bound(trans)) {
+                               nft_trans_destroy(trans);
+                               break;
+                       }
                        trans->ctx.chain->use--;
                        list_del_rcu(&nft_trans_rule(trans)->list);
                        nft_rule_expr_deactivate(&trans->ctx,
@@ -9688,6 +9906,9 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                case NFT_MSG_DESTROYSET:
                        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 +9920,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 +9936,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:
@@ -9751,6 +9983,8 @@ 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,
@@ -10204,22 +10438,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);
                break;
        }
 }
@@ -10454,6 +10678,9 @@ static void __nft_release_table(struct net *net, struct nft_table *table)
        list_for_each_entry_safe(set, ns, &table->sets, list) {
                list_del(&set->list);
                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) {