]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/netfilter/nf_tables_api.c
netfilter: nf_tables: disallow jump to implicit chain from set element
[mirror_ubuntu-jammy-kernel.git] / net / netfilter / nf_tables_api.c
index c0851fec11d46532f87423e1ab38988bf4418172..8bc4460b627aef8671d7d6267fba579a824a8e1e 100644 (file)
@@ -32,7 +32,6 @@ static LIST_HEAD(nf_tables_objects);
 static LIST_HEAD(nf_tables_flowtables);
 static LIST_HEAD(nf_tables_destroy_list);
 static DEFINE_SPINLOCK(nf_tables_destroy_list_lock);
-static u64 table_handle;
 
 enum {
        NFT_VALIDATE_SKIP       = 0,
@@ -153,6 +152,7 @@ static struct nft_trans *nft_trans_alloc_gfp(const struct nft_ctx *ctx,
        if (trans == NULL)
                return NULL;
 
+       INIT_LIST_HEAD(&trans->list);
        trans->msg_type = msg_type;
        trans->ctx      = *ctx;
 
@@ -222,12 +222,18 @@ err_register:
 }
 
 static void nft_netdev_unregister_hooks(struct net *net,
-                                       struct list_head *hook_list)
+                                       struct list_head *hook_list,
+                                       bool release_netdev)
 {
-       struct nft_hook *hook;
+       struct nft_hook *hook, *next;
 
-       list_for_each_entry(hook, hook_list, list)
+       list_for_each_entry_safe(hook, next, hook_list, list) {
                nf_unregister_net_hook(net, &hook->ops);
+               if (release_netdev) {
+                       list_del(&hook->list);
+                       kfree_rcu(hook, rcu);
+               }
+       }
 }
 
 static int nf_tables_register_hook(struct net *net,
@@ -253,9 +259,10 @@ static int nf_tables_register_hook(struct net *net,
        return nf_register_net_hook(net, &basechain->ops);
 }
 
-static void nf_tables_unregister_hook(struct net *net,
-                                     const struct nft_table *table,
-                                     struct nft_chain *chain)
+static void __nf_tables_unregister_hook(struct net *net,
+                                       const struct nft_table *table,
+                                       struct nft_chain *chain,
+                                       bool release_netdev)
 {
        struct nft_base_chain *basechain;
        const struct nf_hook_ops *ops;
@@ -270,11 +277,19 @@ static void nf_tables_unregister_hook(struct net *net,
                return basechain->type->ops_unregister(net, ops);
 
        if (nft_base_chain_netdev(table->family, basechain->ops.hooknum))
-               nft_netdev_unregister_hooks(net, &basechain->hook_list);
+               nft_netdev_unregister_hooks(net, &basechain->hook_list,
+                                           release_netdev);
        else
                nf_unregister_net_hook(net, &basechain->ops);
 }
 
+static void nf_tables_unregister_hook(struct net *net,
+                                     const struct nft_table *table,
+                                     struct nft_chain *chain)
+{
+       return __nf_tables_unregister_hook(net, table, chain, false);
+}
+
 static void nft_trans_commit_list_add_tail(struct net *net, struct nft_trans *trans)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
@@ -529,6 +544,7 @@ static int nft_trans_flowtable_add(struct nft_ctx *ctx, int msg_type,
        if (msg_type == NFT_MSG_NEWFLOWTABLE)
                nft_activate_next(ctx->net, flowtable);
 
+       INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
        nft_trans_flowtable(trans) = flowtable;
        nft_trans_commit_list_add_tail(ctx->net, trans);
 
@@ -820,7 +836,7 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
@@ -1139,7 +1155,7 @@ static int nf_tables_newtable(struct sk_buff *skb, const struct nfnl_info *info,
        INIT_LIST_HEAD(&table->flowtables);
        table->family = family;
        table->flags = flags;
-       table->handle = ++table_handle;
+       table->handle = ++nft_net->table_handle;
        if (table->flags & NFT_TABLE_F_OWNER)
                table->nlpid = NETLINK_CB(skb).portid;
 
@@ -1609,7 +1625,7 @@ static int nf_tables_dump_chains(struct sk_buff *skb,
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
@@ -1820,7 +1836,6 @@ static struct nft_hook *nft_netdev_hook_alloc(struct net *net,
                goto err_hook_dev;
        }
        hook->ops.dev = dev;
-       hook->inactive = false;
 
        return hook;
 
@@ -2057,7 +2072,7 @@ static int nft_basechain_init(struct nft_base_chain *basechain, u8 family,
        chain->flags |= NFT_CHAIN_BASE | flags;
        basechain->policy = NF_ACCEPT;
        if (chain->flags & NFT_CHAIN_HW_OFFLOAD &&
-           nft_chain_offload_priority(basechain) < 0)
+           !nft_chain_offload_support(basechain))
                return -EOPNOTSUPP;
 
        flow_block_init(&basechain->flow_block);
@@ -2086,9 +2101,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                              struct netlink_ext_ack *extack)
 {
        const struct nlattr * const *nla = ctx->nla;
+       struct nft_stats __percpu *stats = NULL;
        struct nft_table *table = ctx->table;
        struct nft_base_chain *basechain;
-       struct nft_stats __percpu *stats;
        struct net *net = ctx->net;
        char name[NFT_NAME_MAXLEN];
        struct nft_trans *trans;
@@ -2125,7 +2140,6 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                                return PTR_ERR(stats);
                        }
                        rcu_assign_pointer(basechain->stats, stats);
-                       static_branch_inc(&nft_counters_enabled);
                }
 
                err = nft_basechain_init(basechain, family, &hook, flags);
@@ -2208,6 +2222,9 @@ static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask,
                goto err_unregister_hook;
        }
 
+       if (stats)
+               static_branch_inc(&nft_counters_enabled);
+
        table->use++;
 
        return 0;
@@ -2362,6 +2379,7 @@ err:
 }
 
 static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
+                                              const struct nft_table *table,
                                               const struct nlattr *nla)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
@@ -2372,6 +2390,7 @@ static struct nft_chain *nft_chain_lookup_byid(const struct net *net,
                struct nft_chain *chain = trans->ctx.chain;
 
                if (trans->msg_type == NFT_MSG_NEWCHAIN &&
+                   chain->table == table &&
                    id == nft_trans_chain_id(trans))
                        return chain;
        }
@@ -2461,6 +2480,9 @@ static int nf_tables_newchain(struct sk_buff *skb, const struct nfnl_info *info,
        nft_ctx_init(&ctx, net, skb, info->nlh, family, table, chain, nla);
 
        if (chain != NULL) {
+               if (chain->flags & NFT_CHAIN_BINDING)
+                       return -EINVAL;
+
                if (info->nlh->nlmsg_flags & NLM_F_EXCL) {
                        NL_SET_BAD_ATTR(extack, attr);
                        return -EEXIST;
@@ -2778,27 +2800,31 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
 
        err = nf_tables_expr_parse(ctx, nla, &expr_info);
        if (err < 0)
-               goto err1;
+               goto err_expr_parse;
+
+       err = -EOPNOTSUPP;
+       if (!(expr_info.ops->type->flags & NFT_EXPR_STATEFUL))
+               goto err_expr_stateful;
 
        err = -ENOMEM;
        expr = kzalloc(expr_info.ops->size, GFP_KERNEL);
        if (expr == NULL)
-               goto err2;
+               goto err_expr_stateful;
 
        err = nf_tables_newexpr(ctx, &expr_info, expr);
        if (err < 0)
-               goto err3;
+               goto err_expr_new;
 
        return expr;
-err3:
+err_expr_new:
        kfree(expr);
-err2:
+err_expr_stateful:
        owner = expr_info.ops->type->owner;
        if (expr_info.ops->type->release_ops)
                expr_info.ops->type->release_ops(expr_info.ops);
 
        module_put(owner);
-err1:
+err_expr_parse:
        return ERR_PTR(err);
 }
 
@@ -3032,7 +3058,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb,
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
@@ -3255,6 +3281,7 @@ static int nft_table_validate(struct net *net, const struct nft_table *table)
 }
 
 static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
+                                            const struct nft_chain *chain,
                                             const struct nlattr *nla);
 
 #define NFT_RULE_MAXEXPRS      128
@@ -3301,7 +3328,7 @@ 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, nla[NFTA_RULE_CHAIN_ID]);
+               chain = nft_chain_lookup_byid(net, table, nla[NFTA_RULE_CHAIN_ID]);
                if (IS_ERR(chain)) {
                        NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_CHAIN_ID]);
                        return PTR_ERR(chain);
@@ -3343,7 +3370,7 @@ static int nf_tables_newrule(struct sk_buff *skb, const struct nfnl_info *info,
                                return PTR_ERR(old_rule);
                        }
                } else if (nla[NFTA_RULE_POSITION_ID]) {
-                       old_rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_POSITION_ID]);
+                       old_rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_POSITION_ID]);
                        if (IS_ERR(old_rule)) {
                                NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_POSITION_ID]);
                                return PTR_ERR(old_rule);
@@ -3488,6 +3515,7 @@ err_release_expr:
 }
 
 static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
+                                            const struct nft_chain *chain,
                                             const struct nlattr *nla)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
@@ -3498,6 +3526,7 @@ static struct nft_rule *nft_rule_lookup_byid(const struct net *net,
                struct nft_rule *rule = nft_trans_rule(trans);
 
                if (trans->msg_type == NFT_MSG_NEWRULE &&
+                   trans->ctx.chain == chain &&
                    id == nft_trans_rule_id(trans))
                        return rule;
        }
@@ -3547,7 +3576,7 @@ static int nf_tables_delrule(struct sk_buff *skb, const struct nfnl_info *info,
 
                        err = nft_delrule(&ctx, rule);
                } else if (nla[NFTA_RULE_ID]) {
-                       rule = nft_rule_lookup_byid(net, nla[NFTA_RULE_ID]);
+                       rule = nft_rule_lookup_byid(net, chain, nla[NFTA_RULE_ID]);
                        if (IS_ERR(rule)) {
                                NL_SET_BAD_ATTR(extack, nla[NFTA_RULE_ID]);
                                return PTR_ERR(rule);
@@ -3726,6 +3755,7 @@ static struct nft_set *nft_set_lookup_byhandle(const struct nft_table *table,
 }
 
 static struct nft_set *nft_set_lookup_byid(const struct net *net,
+                                          const struct nft_table *table,
                                           const struct nlattr *nla, u8 genmask)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
@@ -3737,6 +3767,7 @@ static struct nft_set *nft_set_lookup_byid(const struct net *net,
                        struct nft_set *set = nft_trans_set(trans);
 
                        if (id == nft_trans_set_id(trans) &&
+                           set->table == table &&
                            nft_active_genmask(set, genmask))
                                return set;
                }
@@ -3757,7 +3788,7 @@ struct nft_set *nft_set_lookup_global(const struct net *net,
                if (!nla_set_id)
                        return set;
 
-               set = nft_set_lookup_byid(net, nla_set_id, genmask);
+               set = nft_set_lookup_byid(net, table, nla_set_id, genmask);
        }
        return set;
 }
@@ -3783,7 +3814,7 @@ cont:
                list_for_each_entry(i, &ctx->table->sets, list) {
                        int tmp;
 
-                       if (!nft_is_active_next(ctx->net, set))
+                       if (!nft_is_active_next(ctx->net, i))
                                continue;
                        if (!sscanf(i->name, name, &tmp))
                                continue;
@@ -4009,7 +4040,7 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (ctx->family != NFPROTO_UNSPEC &&
@@ -4147,6 +4178,9 @@ static int nft_set_desc_concat_parse(const struct nlattr *attr,
        u32 len;
        int err;
 
+       if (desc->field_count >= ARRAY_SIZE(desc->field_len))
+               return -E2BIG;
+
        err = nla_parse_nested_deprecated(tb, NFTA_SET_FIELD_MAX, attr,
                                          nft_concat_policy, NULL);
        if (err < 0)
@@ -4156,9 +4190,8 @@ static int nft_set_desc_concat_parse(const struct nlattr *attr,
                return -EINVAL;
 
        len = ntohl(nla_get_be32(tb[NFTA_SET_FIELD_LEN]));
-
-       if (len * BITS_PER_BYTE / 32 > NFT_REG32_COUNT)
-               return -E2BIG;
+       if (!len || len > U8_MAX)
+               return -EINVAL;
 
        desc->field_len[desc->field_count++] = len;
 
@@ -4169,7 +4202,8 @@ static int nft_set_desc_concat(struct nft_set_desc *desc,
                               const struct nlattr *nla)
 {
        struct nlattr *attr;
-       int rem, err;
+       u32 num_regs = 0;
+       int rem, err, i;
 
        nla_for_each_nested(attr, nla, rem) {
                if (nla_type(attr) != NFTA_LIST_ELEM)
@@ -4180,6 +4214,12 @@ static int nft_set_desc_concat(struct nft_set_desc *desc,
                        return err;
        }
 
+       for (i = 0; i < desc->field_count; i++)
+               num_regs += DIV_ROUND_UP(desc->field_len[i], sizeof(u32));
+
+       if (num_regs > NFT_REG32_COUNT)
+               return -E2BIG;
+
        return 0;
 }
 
@@ -4318,6 +4358,11 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
                err = nf_tables_set_desc_parse(&desc, nla[NFTA_SET_DESC]);
                if (err < 0)
                        return err;
+
+               if (desc.field_count > 1 && !(flags & NFT_SET_CONCAT))
+                       return -EINVAL;
+       } else if (flags & NFT_SET_CONCAT) {
+               return -EINVAL;
        }
 
        if (nla[NFTA_SET_EXPR] || nla[NFTA_SET_EXPRESSIONS])
@@ -4481,12 +4526,12 @@ struct nft_set_elem_catchall {
 static void nft_set_catchall_destroy(const struct nft_ctx *ctx,
                                     struct nft_set *set)
 {
-       struct nft_set_elem_catchall *catchall;
+       struct nft_set_elem_catchall *next, *catchall;
 
-       list_for_each_entry_rcu(catchall, &set->catchall_list, list) {
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                list_del_rcu(&catchall->list);
                nft_set_elem_destroy(set, catchall->elem, true);
-               kfree_rcu(catchall);
+               kfree_rcu(catchall, rcu);
        }
 }
 
@@ -4928,6 +4973,8 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
+       cb->seq = READ_ONCE(nft_net->base_seq);
+
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (dump_ctx->ctx.family != NFPROTO_UNSPEC &&
                    dump_ctx->ctx.family != table->family)
@@ -5063,6 +5110,9 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
        if (!(set->flags & NFT_SET_INTERVAL) &&
            *flags & NFT_SET_ELEM_INTERVAL_END)
                return -EINVAL;
+       if ((*flags & (NFT_SET_ELEM_INTERVAL_END | NFT_SET_ELEM_CATCHALL)) ==
+           (NFT_SET_ELEM_INTERVAL_END | NFT_SET_ELEM_CATCHALL))
+               return -EINVAL;
 
        return 0;
 }
@@ -5070,19 +5120,13 @@ static int nft_setelem_parse_flags(const struct nft_set *set,
 static int nft_setelem_parse_key(struct nft_ctx *ctx, struct nft_set *set,
                                 struct nft_data *key, struct nlattr *attr)
 {
-       struct nft_data_desc desc;
-       int err;
-
-       err = nft_data_init(ctx, key, NFT_DATA_VALUE_MAXLEN, &desc, attr);
-       if (err < 0)
-               return err;
-
-       if (desc.type != NFT_DATA_VALUE || desc.len != set->klen) {
-               nft_data_release(key, desc.type);
-               return -EINVAL;
-       }
+       struct nft_data_desc desc = {
+               .type   = NFT_DATA_VALUE,
+               .size   = NFT_DATA_VALUE_MAXLEN,
+               .len    = set->klen,
+       };
 
-       return 0;
+       return nft_data_init(ctx, key, &desc, attr);
 }
 
 static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
@@ -5090,18 +5134,19 @@ static int nft_setelem_parse_data(struct nft_ctx *ctx, struct nft_set *set,
                                  struct nft_data *data,
                                  struct nlattr *attr)
 {
-       int err;
+       u32 dtype;
 
-       err = nft_data_init(ctx, data, NFT_DATA_VALUE_MAXLEN, desc, attr);
-       if (err < 0)
-               return err;
+       if (set->dtype == NFT_DATA_VERDICT)
+               dtype = NFT_DATA_VERDICT;
+       else
+               dtype = NFT_DATA_VALUE;
 
-       if (desc->type != NFT_DATA_VERDICT && desc->len != set->dlen) {
-               nft_data_release(data, desc->type);
-               return -EINVAL;
-       }
+       desc->type = dtype;
+       desc->size = NFT_DATA_VALUE_MAXLEN;
+       desc->len = set->dlen;
+       desc->flags = NFT_DATA_DESC_SETELEM;
 
-       return 0;
+       return nft_data_init(ctx, data, desc, attr);
 }
 
 static void *nft_setelem_catchall_get(const struct net *net,
@@ -5318,9 +5363,6 @@ struct nft_expr *nft_set_elem_expr_alloc(const struct nft_ctx *ctx,
                return expr;
 
        err = -EOPNOTSUPP;
-       if (!(expr->ops->type->flags & NFT_EXPR_STATEFUL))
-               goto err_set_elem_expr;
-
        if (expr->ops->type->flags & NFT_EXPR_GC) {
                if (set->flags & NFT_SET_TIMEOUT)
                        goto err_set_elem_expr;
@@ -5437,7 +5479,7 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
 
                err = nft_expr_clone(expr, set->exprs[i]);
                if (err < 0) {
-                       nft_expr_destroy(ctx, expr);
+                       kfree(expr);
                        goto err_expr;
                }
                expr_array[i] = expr;
@@ -5653,7 +5695,7 @@ static void nft_setelem_catchall_remove(const struct net *net,
        list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
                if (catchall->elem == elem->priv) {
                        list_del_rcu(&catchall->list);
-                       kfree_rcu(catchall);
+                       kfree_rcu(catchall, rcu);
                        break;
                }
        }
@@ -5669,6 +5711,24 @@ static void nft_setelem_remove(const struct net *net,
                set->ops->remove(net, set, elem);
 }
 
+static bool nft_setelem_valid_key_end(const struct nft_set *set,
+                                     struct nlattr **nla, u32 flags)
+{
+       if ((set->flags & (NFT_SET_CONCAT | NFT_SET_INTERVAL)) ==
+                         (NFT_SET_CONCAT | NFT_SET_INTERVAL)) {
+               if (flags & NFT_SET_ELEM_INTERVAL_END)
+                       return false;
+               if (!nla[NFTA_SET_ELEM_KEY_END] &&
+                   !(flags & NFT_SET_ELEM_CATCHALL))
+                       return false;
+       } else {
+               if (nla[NFTA_SET_ELEM_KEY_END])
+                       return false;
+       }
+
+       return true;
+}
+
 static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                            const struct nlattr *attr, u32 nlmsg_flags)
 {
@@ -5704,8 +5764,11 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
                return -EINVAL;
 
-       if (flags != 0)
-               nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
+       if (flags != 0) {
+               err = nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
+               if (err < 0)
+                       return err;
+       }
 
        if (set->flags & NFT_SET_MAP) {
                if (nla[NFTA_SET_ELEM_DATA] == NULL &&
@@ -5716,6 +5779,18 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        return -EINVAL;
        }
 
+       if (set->flags & NFT_SET_OBJECT) {
+               if (!nla[NFTA_SET_ELEM_OBJREF] &&
+                   !(flags & NFT_SET_ELEM_INTERVAL_END))
+                       return -EINVAL;
+       } else {
+               if (nla[NFTA_SET_ELEM_OBJREF])
+                       return -EINVAL;
+       }
+
+       if (!nft_setelem_valid_key_end(set, nla, flags))
+               return -EINVAL;
+
        if ((flags & NFT_SET_ELEM_INTERVAL_END) &&
             (nla[NFTA_SET_ELEM_DATA] ||
              nla[NFTA_SET_ELEM_OBJREF] ||
@@ -5723,6 +5798,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
              nla[NFTA_SET_ELEM_EXPIRATION] ||
              nla[NFTA_SET_ELEM_USERDATA] ||
              nla[NFTA_SET_ELEM_EXPR] ||
+             nla[NFTA_SET_ELEM_KEY_END] ||
              nla[NFTA_SET_ELEM_EXPRESSIONS]))
                return -EINVAL;
 
@@ -5814,7 +5890,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                if (err < 0)
                        goto err_set_elem_expr;
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               if (err < 0)
+                       goto err_parse_key;
        }
 
        if (nla[NFTA_SET_ELEM_KEY_END]) {
@@ -5823,29 +5901,34 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                if (err < 0)
                        goto err_parse_key;
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+               if (err < 0)
+                       goto err_parse_key_end;
        }
 
        if (timeout > 0) {
-               nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
-               if (timeout != set->timeout)
-                       nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
+               err = nft_set_ext_add(&tmpl, NFT_SET_EXT_EXPIRATION);
+               if (err < 0)
+                       goto err_parse_key_end;
+
+               if (timeout != set->timeout) {
+                       err = nft_set_ext_add(&tmpl, NFT_SET_EXT_TIMEOUT);
+                       if (err < 0)
+                               goto err_parse_key_end;
+               }
        }
 
        if (num_exprs) {
                for (i = 0; i < num_exprs; i++)
                        size += expr_array[i]->ops->size;
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS,
-                                      sizeof(struct nft_set_elem_expr) +
-                                      size);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_EXPRESSIONS,
+                                            sizeof(struct nft_set_elem_expr) + size);
+               if (err < 0)
+                       goto err_parse_key_end;
        }
 
        if (nla[NFTA_SET_ELEM_OBJREF] != NULL) {
-               if (!(set->flags & NFT_SET_OBJECT)) {
-                       err = -EINVAL;
-                       goto err_parse_key_end;
-               }
                obj = nft_obj_lookup(ctx->net, ctx->table,
                                     nla[NFTA_SET_ELEM_OBJREF],
                                     set->objtype, genmask);
@@ -5853,7 +5936,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                        err = PTR_ERR(obj);
                        goto err_parse_key_end;
                }
-               nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
+               err = nft_set_ext_add(&tmpl, NFT_SET_EXT_OBJREF);
+               if (err < 0)
+                       goto err_parse_key_end;
        }
 
        if (nla[NFTA_SET_ELEM_DATA] != NULL) {
@@ -5887,7 +5972,9 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
                                                          NFT_VALIDATE_NEED);
                }
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, desc.len);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_DATA, desc.len);
+               if (err < 0)
+                       goto err_parse_data;
        }
 
        /* The full maximum length of userdata can exceed the maximum
@@ -5897,9 +5984,12 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
        ulen = 0;
        if (nla[NFTA_SET_ELEM_USERDATA] != NULL) {
                ulen = nla_len(nla[NFTA_SET_ELEM_USERDATA]);
-               if (ulen > 0)
-                       nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA,
-                                              ulen);
+               if (ulen > 0) {
+                       err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_USERDATA,
+                                                    ulen);
+                       if (err < 0)
+                               goto err_parse_data;
+               }
        }
 
        err = -ENOMEM;
@@ -6123,10 +6213,16 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
        if (!nla[NFTA_SET_ELEM_KEY] && !(flags & NFT_SET_ELEM_CATCHALL))
                return -EINVAL;
 
+       if (!nft_setelem_valid_key_end(set, nla, flags))
+               return -EINVAL;
+
        nft_set_ext_prepare(&tmpl);
 
-       if (flags != 0)
-               nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
+       if (flags != 0) {
+               err = nft_set_ext_add(&tmpl, NFT_SET_EXT_FLAGS);
+               if (err < 0)
+                       return err;
+       }
 
        if (nla[NFTA_SET_ELEM_KEY]) {
                err = nft_setelem_parse_key(ctx, set, &elem.key.val,
@@ -6134,16 +6230,20 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                if (err < 0)
                        return err;
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY, set->klen);
+               if (err < 0)
+                       goto fail_elem;
        }
 
        if (nla[NFTA_SET_ELEM_KEY_END]) {
                err = nft_setelem_parse_key(ctx, set, &elem.key_end.val,
                                            nla[NFTA_SET_ELEM_KEY_END]);
                if (err < 0)
-                       return err;
+                       goto fail_elem;
 
-               nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+               err = nft_set_ext_add_length(&tmpl, NFT_SET_EXT_KEY_END, set->klen);
+               if (err < 0)
+                       goto fail_elem_key_end;
        }
 
        err = -ENOMEM;
@@ -6151,7 +6251,7 @@ static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
                                      elem.key_end.val.data, NULL, 0, 0,
                                      GFP_KERNEL);
        if (elem.priv == NULL)
-               goto fail_elem;
+               goto fail_elem_key_end;
 
        ext = nft_set_elem_ext(set, elem.priv);
        if (flags)
@@ -6175,6 +6275,8 @@ fail_ops:
        kfree(trans);
 fail_trans:
        kfree(elem.priv);
+fail_elem_key_end:
+       nft_data_release(&elem.key_end.val, NFT_DATA_VALUE);
 fail_elem:
        nft_data_release(&elem.key.val, NFT_DATA_VALUE);
        return err;
@@ -6535,12 +6637,15 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
 {
        struct nft_object *newobj;
        struct nft_trans *trans;
-       int err;
+       int err = -ENOMEM;
+
+       if (!try_module_get(type->owner))
+               return -ENOENT;
 
        trans = nft_trans_alloc(ctx, NFT_MSG_NEWOBJ,
                                sizeof(struct nft_trans_obj));
        if (!trans)
-               return -ENOMEM;
+               goto err_trans;
 
        newobj = nft_obj_init(ctx, type, attr);
        if (IS_ERR(newobj)) {
@@ -6557,6 +6662,8 @@ static int nf_tables_updobj(const struct nft_ctx *ctx,
 
 err_free_trans:
        kfree(trans);
+err_trans:
+       module_put(type->owner);
        return err;
 }
 
@@ -6721,7 +6828,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb)
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
@@ -7191,13 +7298,25 @@ static void nft_unregister_flowtable_hook(struct net *net,
                                    FLOW_BLOCK_UNBIND);
 }
 
-static void nft_unregister_flowtable_net_hooks(struct net *net,
-                                              struct list_head *hook_list)
+static void __nft_unregister_flowtable_net_hooks(struct net *net,
+                                                struct list_head *hook_list,
+                                                bool release_netdev)
 {
-       struct nft_hook *hook;
+       struct nft_hook *hook, *next;
 
-       list_for_each_entry(hook, hook_list, list)
+       list_for_each_entry_safe(hook, next, hook_list, list) {
                nf_unregister_net_hook(net, &hook->ops);
+               if (release_netdev) {
+                       list_del(&hook->list);
+                       kfree_rcu(hook, rcu);
+               }
+       }
+}
+
+static void nft_unregister_flowtable_net_hooks(struct net *net,
+                                              struct list_head *hook_list)
+{
+       __nft_unregister_flowtable_net_hooks(net, hook_list, false);
 }
 
 static int nft_register_flowtable_net_hooks(struct net *net,
@@ -7290,11 +7409,15 @@ static int nft_flowtable_update(struct nft_ctx *ctx, const struct nlmsghdr *nlh,
 
        if (nla[NFTA_FLOWTABLE_FLAGS]) {
                flags = ntohl(nla_get_be32(nla[NFTA_FLOWTABLE_FLAGS]));
-               if (flags & ~NFT_FLOWTABLE_MASK)
-                       return -EOPNOTSUPP;
+               if (flags & ~NFT_FLOWTABLE_MASK) {
+                       err = -EOPNOTSUPP;
+                       goto err_flowtable_update_hook;
+               }
                if ((flowtable->data.flags & NFT_FLOWTABLE_HW_OFFLOAD) ^
-                   (flags & NFT_FLOWTABLE_HW_OFFLOAD))
-                       return -EOPNOTSUPP;
+                   (flags & NFT_FLOWTABLE_HW_OFFLOAD)) {
+                       err = -EOPNOTSUPP;
+                       goto err_flowtable_update_hook;
+               }
        } else {
                flags = flowtable->data.flags;
        }
@@ -7475,6 +7598,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
 {
        const struct nlattr * const *nla = ctx->nla;
        struct nft_flowtable_hook flowtable_hook;
+       LIST_HEAD(flowtable_del_list);
        struct nft_hook *this, *hook;
        struct nft_trans *trans;
        int err;
@@ -7490,7 +7614,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
                        err = -ENOENT;
                        goto err_flowtable_del_hook;
                }
-               hook->inactive = true;
+               list_move(&hook->list, &flowtable_del_list);
        }
 
        trans = nft_trans_alloc(ctx, NFT_MSG_DELFLOWTABLE,
@@ -7503,6 +7627,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
        nft_trans_flowtable(trans) = flowtable;
        nft_trans_flowtable_update(trans) = true;
        INIT_LIST_HEAD(&nft_trans_flowtable_hooks(trans));
+       list_splice(&flowtable_del_list, &nft_trans_flowtable_hooks(trans));
        nft_flowtable_hook_release(&flowtable_hook);
 
        nft_trans_commit_list_add_tail(ctx->net, trans);
@@ -7510,13 +7635,7 @@ static int nft_delflowtable_hook(struct nft_ctx *ctx,
        return 0;
 
 err_flowtable_del_hook:
-       list_for_each_entry(this, &flowtable_hook.list, list) {
-               hook = nft_hook_list_find(&flowtable->hook_list, this);
-               if (!hook)
-                       break;
-
-               hook->inactive = false;
-       }
+       list_splice(&flowtable_del_list, &flowtable->hook_list);
        nft_flowtable_hook_release(&flowtable_hook);
 
        return err;
@@ -7641,7 +7760,7 @@ static int nf_tables_dump_flowtable(struct sk_buff *skb,
 
        rcu_read_lock();
        nft_net = nft_pernet(net);
-       cb->seq = nft_net->base_seq;
+       cb->seq = READ_ONCE(nft_net->base_seq);
 
        list_for_each_entry_rcu(table, &nft_net->tables, list) {
                if (family != NFPROTO_UNSPEC && family != table->family)
@@ -8169,7 +8288,7 @@ static void nft_obj_commit_update(struct nft_trans *trans)
        if (obj->ops->update)
                obj->ops->update(obj, newobj);
 
-       kfree(newobj);
+       nft_obj_destroy(&trans->ctx, newobj);
 }
 
 static void nft_commit_release(struct nft_trans *trans)
@@ -8186,6 +8305,9 @@ static void nft_commit_release(struct nft_trans *trans)
                nf_tables_chain_destroy(&trans->ctx);
                break;
        case NFT_MSG_DELRULE:
+               if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
+                       nft_flow_rule_destroy(nft_trans_flow_rule(trans));
+
                nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
                break;
        case NFT_MSG_DELSET:
@@ -8371,17 +8493,6 @@ void nft_chain_del(struct nft_chain *chain)
        list_del_rcu(&chain->list);
 }
 
-static void nft_flowtable_hooks_del(struct nft_flowtable *flowtable,
-                                   struct list_head *hook_list)
-{
-       struct nft_hook *hook, *next;
-
-       list_for_each_entry_safe(hook, next, &flowtable->hook_list, list) {
-               if (hook->inactive)
-                       list_move(&hook->list, hook_list);
-       }
-}
-
 static void nf_tables_module_autoload_cleanup(struct net *net)
 {
        struct nftables_pernet *nft_net = nft_pernet(net);
@@ -8533,6 +8644,7 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
        struct nft_trans_elem *te;
        struct nft_chain *chain;
        struct nft_table *table;
+       unsigned int base_seq;
        LIST_HEAD(adl);
        int err;
 
@@ -8582,9 +8694,12 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
         * Bump generation counter, invalidate any dump in progress.
         * Cannot fail after this point.
         */
-       while (++nft_net->base_seq == 0)
+       base_seq = READ_ONCE(nft_net->base_seq);
+       while (++base_seq == 0)
                ;
 
+       WRITE_ONCE(nft_net->base_seq, base_seq);
+
        /* step 3. Start new generation, rules_gen_X now in use. */
        net->nft.gencursor = nft_gencursor_next(net);
 
@@ -8636,6 +8751,9 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        nf_tables_rule_notify(&trans->ctx,
                                              nft_trans_rule(trans),
                                              NFT_MSG_NEWRULE);
+                       if (trans->ctx.chain->flags & NFT_CHAIN_HW_OFFLOAD)
+                               nft_flow_rule_destroy(nft_trans_flow_rule(trans));
+
                        nft_trans_destroy(trans);
                        break;
                case NFT_MSG_DELRULE:
@@ -8726,8 +8844,6 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
                        break;
                case NFT_MSG_DELFLOWTABLE:
                        if (nft_trans_flowtable_update(trans)) {
-                               nft_flowtable_hooks_del(nft_trans_flowtable(trans),
-                                                       &nft_trans_flowtable_hooks(trans));
                                nf_tables_flowtable_notify(&trans->ctx,
                                                           nft_trans_flowtable(trans),
                                                           &nft_trans_flowtable_hooks(trans),
@@ -8808,7 +8924,6 @@ 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;
        struct nft_trans_elem *te;
-       struct nft_hook *hook;
 
        if (action == NFNL_ABORT_VALIDATE &&
            nf_tables_validate(net) < 0)
@@ -8914,7 +9029,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        break;
                case NFT_MSG_NEWOBJ:
                        if (nft_trans_obj_update(trans)) {
-                               kfree(nft_trans_obj_newobj(trans));
+                               nft_obj_destroy(&trans->ctx, nft_trans_obj_newobj(trans));
                                nft_trans_destroy(trans);
                        } else {
                                trans->ctx.table->use--;
@@ -8939,8 +9054,8 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                        break;
                case NFT_MSG_DELFLOWTABLE:
                        if (nft_trans_flowtable_update(trans)) {
-                               list_for_each_entry(hook, &nft_trans_flowtable(trans)->hook_list, list)
-                                       hook->inactive = false;
+                               list_splice(&nft_trans_flowtable_hooks(trans),
+                                           &nft_trans_flowtable(trans)->hook_list);
                        } else {
                                trans->ctx.table->use++;
                                nft_clear(trans->ctx.net, nft_trans_flowtable(trans));
@@ -9203,17 +9318,23 @@ int nft_parse_u32_check(const struct nlattr *attr, int max, u32 *dest)
 }
 EXPORT_SYMBOL_GPL(nft_parse_u32_check);
 
-static unsigned int nft_parse_register(const struct nlattr *attr)
+static int nft_parse_register(const struct nlattr *attr, u32 *preg)
 {
        unsigned int reg;
 
        reg = ntohl(nla_get_be32(attr));
        switch (reg) {
        case NFT_REG_VERDICT...NFT_REG_4:
-               return reg * NFT_REG_SIZE / NFT_REG32_SIZE;
+               *preg = reg * NFT_REG_SIZE / NFT_REG32_SIZE;
+               break;
+       case NFT_REG32_00...NFT_REG32_15:
+               *preg = reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
+               break;
        default:
-               return reg + NFT_REG_SIZE / NFT_REG32_SIZE - NFT_REG32_00;
+               return -ERANGE;
        }
+
+       return 0;
 }
 
 /**
@@ -9255,7 +9376,10 @@ int nft_parse_register_load(const struct nlattr *attr, u8 *sreg, u32 len)
        u32 reg;
        int err;
 
-       reg = nft_parse_register(attr);
+       err = nft_parse_register(attr, &reg);
+       if (err < 0)
+               return err;
+
        err = nft_validate_register_load(reg, len);
        if (err < 0)
                return err;
@@ -9310,7 +9434,10 @@ int nft_parse_register_store(const struct nft_ctx *ctx,
        int err;
        u32 reg;
 
-       reg = nft_parse_register(attr);
+       err = nft_parse_register(attr, &reg);
+       if (err < 0)
+               return err;
+
        err = nft_validate_register_store(ctx, reg, data, type, len);
        if (err < 0)
                return err;
@@ -9366,7 +9493,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
                                                 tb[NFTA_VERDICT_CHAIN],
                                                 genmask);
                } else if (tb[NFTA_VERDICT_CHAIN_ID]) {
-                       chain = nft_chain_lookup_byid(ctx->net,
+                       chain = nft_chain_lookup_byid(ctx->net, ctx->table,
                                                      tb[NFTA_VERDICT_CHAIN_ID]);
                        if (IS_ERR(chain))
                                return PTR_ERR(chain);
@@ -9378,6 +9505,9 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
                        return PTR_ERR(chain);
                if (nft_is_base_chain(chain))
                        return -EOPNOTSUPP;
+               if (desc->flags & NFT_DATA_DESC_SETELEM &&
+                   chain->flags & NFT_CHAIN_BINDING)
+                       return -EINVAL;
 
                chain->use++;
                data->verdict.chain = chain;
@@ -9385,7 +9515,7 @@ static int nft_verdict_init(const struct nft_ctx *ctx, struct nft_data *data,
        }
 
        desc->len = sizeof(data->verdict);
-       desc->type = NFT_DATA_VERDICT;
+
        return 0;
 }
 
@@ -9438,20 +9568,25 @@ nla_put_failure:
 }
 
 static int nft_value_init(const struct nft_ctx *ctx,
-                         struct nft_data *data, unsigned int size,
-                         struct nft_data_desc *desc, const struct nlattr *nla)
+                         struct nft_data *data, struct nft_data_desc *desc,
+                         const struct nlattr *nla)
 {
        unsigned int len;
 
        len = nla_len(nla);
        if (len == 0)
                return -EINVAL;
-       if (len > size)
+       if (len > desc->size)
                return -EOVERFLOW;
+       if (desc->len) {
+               if (len != desc->len)
+                       return -EINVAL;
+       } else {
+               desc->len = len;
+       }
 
        nla_memcpy(data->data, nla, len);
-       desc->type = NFT_DATA_VALUE;
-       desc->len  = len;
+
        return 0;
 }
 
@@ -9471,7 +9606,6 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
  *
  *     @ctx: context of the expression using the data
  *     @data: destination struct nft_data
- *     @size: maximum data length
  *     @desc: data description
  *     @nla: netlink attribute containing data
  *
@@ -9481,24 +9615,35 @@ static const struct nla_policy nft_data_policy[NFTA_DATA_MAX + 1] = {
  *     The caller can indicate that it only wants to accept data of type
  *     NFT_DATA_VALUE by passing NULL for the ctx argument.
  */
-int nft_data_init(const struct nft_ctx *ctx,
-                 struct nft_data *data, unsigned int size,
+int nft_data_init(const struct nft_ctx *ctx, struct nft_data *data,
                  struct nft_data_desc *desc, const struct nlattr *nla)
 {
        struct nlattr *tb[NFTA_DATA_MAX + 1];
        int err;
 
+       if (WARN_ON_ONCE(!desc->size))
+               return -EINVAL;
+
        err = nla_parse_nested_deprecated(tb, NFTA_DATA_MAX, nla,
                                          nft_data_policy, NULL);
        if (err < 0)
                return err;
 
-       if (tb[NFTA_DATA_VALUE])
-               return nft_value_init(ctx, data, size, desc,
-                                     tb[NFTA_DATA_VALUE]);
-       if (tb[NFTA_DATA_VERDICT] && ctx != NULL)
-               return nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
-       return -EINVAL;
+       if (tb[NFTA_DATA_VALUE]) {
+               if (desc->type != NFT_DATA_VALUE)
+                       return -EINVAL;
+
+               err = nft_value_init(ctx, data, desc, tb[NFTA_DATA_VALUE]);
+       } else if (tb[NFTA_DATA_VERDICT] && ctx != NULL) {
+               if (desc->type != NFT_DATA_VERDICT)
+                       return -EINVAL;
+
+               err = nft_verdict_init(ctx, data, desc, tb[NFTA_DATA_VERDICT]);
+       } else {
+               err = -EINVAL;
+       }
+
+       return err;
 }
 EXPORT_SYMBOL_GPL(nft_data_init);
 
@@ -9574,10 +9719,14 @@ EXPORT_SYMBOL_GPL(__nft_release_basechain);
 
 static void __nft_release_hook(struct net *net, struct nft_table *table)
 {
+       struct nft_flowtable *flowtable;
        struct nft_chain *chain;
 
        list_for_each_entry(chain, &table->chains, list)
-               nf_tables_unregister_hook(net, table, chain);
+               __nf_tables_unregister_hook(net, table, chain, true);
+       list_for_each_entry(flowtable, &table->flowtables, list)
+               __nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list,
+                                                    true);
 }
 
 static void __nft_release_hooks(struct net *net)
@@ -9716,7 +9865,11 @@ static int __net_init nf_tables_init_net(struct net *net)
 
 static void __net_exit nf_tables_pre_exit_net(struct net *net)
 {
+       struct nftables_pernet *nft_net = nft_pernet(net);
+
+       mutex_lock(&nft_net->commit_mutex);
        __nft_release_hooks(net);
+       mutex_unlock(&nft_net->commit_mutex);
 }
 
 static void __net_exit nf_tables_exit_net(struct net *net)