Commit b3c0a9ef authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files
Pablo Neira Ayuso says:

====================
Netfilter fixes for net

1) Missing proper sanitization for nft_set_desc_concat_parse().

2) Missing mutex in nf_tables pre_exit path.

3) Possible double hook unregistration from clean_net path.

4) Missing FLOWI_FLAG_ANYSRC flag in flowtable route lookup.
   Fix incorrect source and destination address in case of NAT.
   Patch from wenxu.

* git://git.kernel.org/pub/scm/linux/kernel/git/netfilter/nf:
  netfilter: flowtable: fix nft_flow_route source address for nat case
  netfilter: flowtable: fix missing FLOWI_FLAG_ANYSRC flag
  netfilter: nf_tables: double hook unregistration in netns path
  netfilter: nf_tables: hold mutex on netns pre_exit path
  netfilter: nf_tables: sanitize nft_set_desc_concat_parse()
====================

Link: https://lore.kernel.org/r/20220531215839.84765-1-pablo@netfilter.org


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2e8728c9 97629b23
Loading
Loading
Loading
Loading
+58 −17
Original line number Diff line number Diff line
@@ -222,12 +222,18 @@ static int nft_netdev_register_hooks(struct net *net,
}

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,
static void __nf_tables_unregister_hook(struct net *net,
					const struct nft_table *table,
				      struct nft_chain *chain)
					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);
@@ -4246,6 +4261,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)
@@ -4255,9 +4273,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;

@@ -4268,7 +4285,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)
@@ -4279,6 +4297,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;
}

@@ -7298,13 +7322,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);
		}
	}
}

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,
@@ -9746,9 +9782,10 @@ static void __nft_release_hook(struct net *net, struct nft_table *table)
	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);
		__nft_unregister_flowtable_net_hooks(net, &flowtable->hook_list,
						     true);
}

static void __nft_release_hooks(struct net *net)
@@ -9887,7 +9924,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)
+4 −2
Original line number Diff line number Diff line
@@ -232,19 +232,21 @@ static int nft_flow_route(const struct nft_pktinfo *pkt,
	switch (nft_pf(pkt)) {
	case NFPROTO_IPV4:
		fl.u.ip4.daddr = ct->tuplehash[dir].tuple.src.u3.ip;
		fl.u.ip4.saddr = ct->tuplehash[dir].tuple.dst.u3.ip;
		fl.u.ip4.saddr = ct->tuplehash[!dir].tuple.src.u3.ip;
		fl.u.ip4.flowi4_oif = nft_in(pkt)->ifindex;
		fl.u.ip4.flowi4_iif = this_dst->dev->ifindex;
		fl.u.ip4.flowi4_tos = RT_TOS(ip_hdr(pkt->skb)->tos);
		fl.u.ip4.flowi4_mark = pkt->skb->mark;
		fl.u.ip4.flowi4_flags = FLOWI_FLAG_ANYSRC;
		break;
	case NFPROTO_IPV6:
		fl.u.ip6.daddr = ct->tuplehash[dir].tuple.src.u3.in6;
		fl.u.ip6.saddr = ct->tuplehash[dir].tuple.dst.u3.in6;
		fl.u.ip6.saddr = ct->tuplehash[!dir].tuple.src.u3.in6;
		fl.u.ip6.flowi6_oif = nft_in(pkt)->ifindex;
		fl.u.ip6.flowi6_iif = this_dst->dev->ifindex;
		fl.u.ip6.flowlabel = ip6_flowinfo(ipv6_hdr(pkt->skb));
		fl.u.ip6.flowi6_mark = pkt->skb->mark;
		fl.u.ip6.flowi6_flags = FLOWI_FLAG_ANYSRC;
		break;
	}