netfilter: nf_tables: reject NFT_SET_CONCAT with not field length description
[platform/kernel/linux-starfive.git] / net / netfilter / nf_tables_api.c
index 29c6518..b28fbcb 100644 (file)
@@ -805,7 +805,7 @@ static struct nft_table *nft_table_lookup(const struct net *net,
 
 static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
                                                   const struct nlattr *nla,
-                                                  u8 genmask, u32 nlpid)
+                                                  int family, u8 genmask, u32 nlpid)
 {
        struct nftables_pernet *nft_net;
        struct nft_table *table;
@@ -813,6 +813,7 @@ static struct nft_table *nft_table_lookup_byhandle(const struct net *net,
        nft_net = nft_pernet(net);
        list_for_each_entry(table, &nft_net->tables, list) {
                if (be64_to_cpu(nla_get_be64(nla)) == table->handle &&
+                   table->family == family &&
                    nft_active_genmask(table, genmask)) {
                        if (nft_table_has_owner(table) &&
                            nlpid && table->nlpid != nlpid)
@@ -1546,7 +1547,7 @@ static int nf_tables_deltable(struct sk_buff *skb, const struct nfnl_info *info,
 
        if (nla[NFTA_TABLE_HANDLE]) {
                attr = nla[NFTA_TABLE_HANDLE];
-               table = nft_table_lookup_byhandle(net, attr, genmask,
+               table = nft_table_lookup_byhandle(net, attr, family, genmask,
                                                  NETLINK_CB(skb).portid);
        } else {
                attr = nla[NFTA_TABLE_NAME];
@@ -2262,7 +2263,16 @@ static int nft_chain_parse_hook(struct net *net,
                                return -EOPNOTSUPP;
                }
 
-               type = basechain->type;
+               if (nla[NFTA_CHAIN_TYPE]) {
+                       type = __nf_tables_chain_type_lookup(nla[NFTA_CHAIN_TYPE],
+                                                            family);
+                       if (!type) {
+                               NL_SET_BAD_ATTR(extack, nla[NFTA_CHAIN_TYPE]);
+                               return -ENOENT;
+                       }
+               } else {
+                       type = basechain->type;
+               }
        }
 
        if (!try_module_get(type->owner)) {
@@ -3465,10 +3475,6 @@ static int __nf_tables_dump_rules(struct sk_buff *skb,
                        goto cont_skip;
                if (*idx < s_idx)
                        goto cont;
-               if (*idx > s_idx) {
-                       memset(&cb->args[1], 0,
-                                       sizeof(cb->args) - sizeof(cb->args[0]));
-               }
                if (prule)
                        handle = prule->handle;
                else
@@ -4747,8 +4753,8 @@ static int nft_set_desc_concat_parse(const struct nlattr *attr,
 static int nft_set_desc_concat(struct nft_set_desc *desc,
                               const struct nlattr *nla)
 {
+       u32 num_regs = 0, key_num_regs = 0;
        struct nlattr *attr;
-       u32 num_regs = 0;
        int rem, err, i;
 
        nla_for_each_nested(attr, nla, rem) {
@@ -4763,6 +4769,10 @@ static int nft_set_desc_concat(struct nft_set_desc *desc,
        for (i = 0; i < desc->field_count; i++)
                num_regs += DIV_ROUND_UP(desc->field_len[i], sizeof(u32));
 
+       key_num_regs = DIV_ROUND_UP(desc->klen, sizeof(u32));
+       if (key_num_regs != num_regs)
+               return -EINVAL;
+
        if (num_regs > NFT_REG32_COUNT)
                return -E2BIG;
 
@@ -4984,16 +4994,28 @@ static int nf_tables_newset(struct sk_buff *skb, const struct nfnl_info *info,
        }
 
        desc.policy = NFT_SET_POL_PERFORMANCE;
-       if (nla[NFTA_SET_POLICY] != NULL)
+       if (nla[NFTA_SET_POLICY] != NULL) {
                desc.policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
+               switch (desc.policy) {
+               case NFT_SET_POL_PERFORMANCE:
+               case NFT_SET_POL_MEMORY:
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+       }
 
        if (nla[NFTA_SET_DESC] != NULL) {
                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))
+               if (desc.field_count > 1) {
+                       if (!(flags & NFT_SET_CONCAT))
+                               return -EINVAL;
+               } else if (flags & NFT_SET_CONCAT) {
                        return -EINVAL;
+               }
        } else if (flags & NFT_SET_CONCAT) {
                return -EINVAL;
        }
@@ -5644,7 +5666,7 @@ static int nf_tables_dump_setelem(const struct nft_ctx *ctx,
        const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
        struct nft_set_dump_args *args;
 
-       if (nft_set_elem_expired(ext))
+       if (nft_set_elem_expired(ext) || nft_set_elem_is_dead(ext))
                return 0;
 
        args = container_of(iter, struct nft_set_dump_args, iter);
@@ -6425,7 +6447,7 @@ static int nft_setelem_catchall_deactivate(const struct net *net,
 
        list_for_each_entry(catchall, &set->catchall_list, list) {
                ext = nft_set_elem_ext(set, catchall->elem);
-               if (!nft_is_active(net, ext))
+               if (!nft_is_active_next(net, ext))
                        continue;
 
                kfree(elem->priv);
@@ -6468,6 +6490,12 @@ static int nft_setelem_deactivate(const struct net *net,
        return ret;
 }
 
+static void nft_setelem_catchall_destroy(struct nft_set_elem_catchall *catchall)
+{
+       list_del_rcu(&catchall->list);
+       kfree_rcu(catchall, rcu);
+}
+
 static void nft_setelem_catchall_remove(const struct net *net,
                                        const struct nft_set *set,
                                        const struct nft_set_elem *elem)
@@ -6476,8 +6504,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, rcu);
+                       nft_setelem_catchall_destroy(catchall);
                        break;
                }
        }
@@ -7209,10 +7236,11 @@ static int nf_tables_delsetelem(struct sk_buff *skb,
 
                if (err < 0) {
                        NL_SET_BAD_ATTR(extack, attr);
-                       break;
+                       return err;
                }
        }
-       return err;
+
+       return 0;
 }
 
 /*
@@ -9638,9 +9666,8 @@ void nft_trans_gc_queue_sync_done(struct nft_trans_gc *trans)
        call_rcu(&trans->rcu, nft_trans_gc_trans_free);
 }
 
-static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
-                                                 unsigned int gc_seq,
-                                                 bool sync)
+struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
+                                                unsigned int gc_seq)
 {
        struct nft_set_elem_catchall *catchall;
        const struct nft_set *set = gc->set;
@@ -9656,11 +9683,7 @@ static struct nft_trans_gc *nft_trans_gc_catchall(struct nft_trans_gc *gc,
 
                nft_set_elem_dead(ext);
 dead_elem:
-               if (sync)
-                       gc = nft_trans_gc_queue_sync(gc, GFP_ATOMIC);
-               else
-                       gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
-
+               gc = nft_trans_gc_queue_async(gc, gc_seq, GFP_ATOMIC);
                if (!gc)
                        return NULL;
 
@@ -9670,15 +9693,34 @@ dead_elem:
        return gc;
 }
 
-struct nft_trans_gc *nft_trans_gc_catchall_async(struct nft_trans_gc *gc,
-                                                unsigned int gc_seq)
-{
-       return nft_trans_gc_catchall(gc, gc_seq, false);
-}
-
 struct nft_trans_gc *nft_trans_gc_catchall_sync(struct nft_trans_gc *gc)
 {
-       return nft_trans_gc_catchall(gc, 0, true);
+       struct nft_set_elem_catchall *catchall, *next;
+       const struct nft_set *set = gc->set;
+       struct nft_set_elem elem;
+       struct nft_set_ext *ext;
+
+       WARN_ON_ONCE(!lockdep_commit_lock_is_held(gc->net));
+
+       list_for_each_entry_safe(catchall, next, &set->catchall_list, list) {
+               ext = nft_set_elem_ext(set, catchall->elem);
+
+               if (!nft_set_elem_expired(ext))
+                       continue;
+
+               gc = nft_trans_gc_queue_sync(gc, GFP_KERNEL);
+               if (!gc)
+                       return NULL;
+
+               memset(&elem, 0, sizeof(elem));
+               elem.priv = catchall->elem;
+
+               nft_setelem_data_deactivate(gc->net, gc->set, &elem);
+               nft_setelem_catchall_destroy(catchall);
+               nft_trans_gc_elem_add(gc, elem.priv);
+       }
+
+       return gc;
 }
 
 static void nf_tables_module_autoload_cleanup(struct net *net)
@@ -9832,7 +9874,7 @@ static void nft_set_commit_update(struct list_head *set_update_list)
        list_for_each_entry_safe(set, next, set_update_list, pending_update) {
                list_del_init(&set->pending_update);
 
-               if (!set->ops->commit)
+               if (!set->ops->commit || set->dead)
                        continue;
 
                set->ops->commit(set);
@@ -10328,6 +10370,7 @@ static int __nf_tables_abort(struct net *net, enum nfnl_abort_action action)
                                nft_trans_destroy(trans);
                                break;
                        }
+                       nft_trans_set(trans)->dead = 1;
                        list_del_rcu(&nft_trans_set(trans)->list);
                        break;
                case NFT_MSG_DELSET: