Skip to contentSkip to conversation

Netfilter/IPVS fixes for net

PATCH In review Pablo Neira Ayuso 2 participants 11 messages Nov 14, 2014 – Nov 16, 2014

Subsystems (4)
lore
Share & tools
lore

Pablo Neira Ayuso

author maintainer
#0 lore raw

cover Netfilter/IPVS fixes for net

Hi David,

The following patchset contains Netfilter updates for your net tree, they are:

1) Fix missing initialization of the range structure (allocated in the stack) in nft_masq_{ipv4, ipv6}_eval, from Daniel Borkmann.

2) Make sure the data we receive from userspace contains the req_version structure, otherwise return an error incomplete on truncated input. From Dan Carpenter.

3) Fix handling og skb->sk which may cause incorrect handling of connections from a local process. Via Simon Horman, patch from Calvin Owens.

4) Fix wrong netns in nft_compat when setting target and match params structure.

5) Relax chain type validation in nft_compat that was recently included, this broke the matches that need to be run from the route chain type. Now iptables-test.py automated regression tests report success again and we avoid the only possible problematic case, which is the use of nat targets out of nat chain type.

6) Use match->table to validate the tablename, instead of the match->name. Again patch for nft_compat.

7) Restore the synchronous release of objects from the commit and abort path in nf_tables. This is causing two major problems: splats when using nft_compat, given that matches and targets may sleep and call_rcu is invoked from softirq context. Moreover Patrick reported possible event notification reordering when rules refer to anonymous sets.

8) Fix race condition in between packets that are being confirmed by conntrack and the ctnetlink flush operation. This happens since the removal of the central spinlock. Thanks to Jesper D. Brouer to looking into this.

You can pull these changes from:

git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git

Thanks!

----------------------------------------------------------------

The following changes since commit d52fdbb735c36a209f36a628d40ca9185b349ba7:

smc91x: retrieve IRQ and trigger flags in a modern way (2014-11-01 17:04:20 -0400)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git master

for you to fetch changes up to 5195c14c8b27cc0b18220ddbf0e5ad3328a04187:

netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse (2014-11-14 17:43:05 +0100)

----------------------------------------------------------------
Calvin Owens (1):

      ipvs: Keep skb->sk when allocating headroom on tunnel xmit

Dan Carpenter (1):
netfilter: ipset: small potential read beyond the end of buffer

Daniel Borkmann (1):
netfilter: nft_masq: fix uninitialized range in nft_masq_{ipv4, ipv6}_eval

Pablo Neira Ayuso (4):
netfilter: nft_compat: use current net namespace
netfilter: nft_compat: relax chain type validation
netfilter: nft_compat: use the match->table to validate dependencies
netfilter: nf_tables: restore synchronous object release from commit/abort

bill bonaparte (1):
netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse

 include/net/netfilter/nf_tables.h  |    2 --
 net/ipv4/netfilter/nft_masq_ipv4.c |    1 +
 net/ipv6/netfilter/nft_masq_ipv6.c |    1 +
 net/netfilter/ipset/ip_set_core.c  |    6 ++++++
 net/netfilter/ipvs/ip_vs_xmit.c    |    2 ++
 net/netfilter/nf_conntrack_core.c  |   14 +++++++------
 net/netfilter/nf_tables_api.c      |   24 ++++++++--------------
 net/netfilter/nft_compat.c         |   40 ++++++------------------------------
 8 files changed, 32 insertions(+), 58 deletions(-)
author maintainer
#1 lore raw

cover Netfilter/IPVS fixes for net

Hi David,

The following patchset contains Netfilter updates for your net tree, they are:

1) Fix missing initialization of the range structure (allocated in the stack) in nft_masq_{ipv4, ipv6}_eval, from Daniel Borkmann.

2) Make sure the data we receive from userspace contains the req_version structure, otherwise return an error incomplete on truncated input. From Dan Carpenter.

3) Fix handling og skb->sk which may cause incorrect handling of connections from a local process. Via Simon Horman, patch from Calvin Owens.

4) Fix wrong netns in nft_compat when setting target and match params structure.

5) Relax chain type validation in nft_compat that was recently included, this broke the matches that need to be run from the route chain type. Now iptables-test.py automated regression tests report success again and we avoid the only possible problematic case, which is the use of nat targets out of nat chain type.

6) Use match->table to validate the tablename, instead of the match->name. Again patch for nft_compat.

7) Restore the synchronous release of objects from the commit and abort path in nf_tables. This is causing two major problems: splats when using nft_compat, given that matches and targets may sleep and call_rcu is invoked from softirq context. Moreover Patrick reported possible event notification reordering when rules refer to anonymous sets.

8) Fix race condition in between packets that are being confirmed by conntrack and the ctnetlink flush operation. This happens since the removal of the central spinlock. Thanks to Jesper D. Brouer to looking into this.

You can pull these changes from:

git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git

Thanks!

----------------------------------------------------------------

The following changes since commit d52fdbb735c36a209f36a628d40ca9185b349ba7:

smc91x: retrieve IRQ and trigger flags in a modern way (2014-11-01 17:04:20 -0400)

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git master

for you to fetch changes up to 5195c14c8b27cc0b18220ddbf0e5ad3328a04187:

netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse (2014-11-14 17:43:05 +0100)

----------------------------------------------------------------
Calvin Owens (1):

      ipvs: Keep skb->sk when allocating headroom on tunnel xmit

Dan Carpenter (1):
netfilter: ipset: small potential read beyond the end of buffer

Daniel Borkmann (1):
netfilter: nft_masq: fix uninitialized range in nft_masq_{ipv4, ipv6}_eval

Pablo Neira Ayuso (4):
netfilter: nft_compat: use current net namespace
netfilter: nft_compat: relax chain type validation
netfilter: nft_compat: use the match->table to validate dependencies
netfilter: nf_tables: restore synchronous object release from commit/abort

bill bonaparte (1):
netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse

 include/net/netfilter/nf_tables.h  |    2 --
 net/ipv4/netfilter/nft_masq_ipv4.c |    1 +
 net/ipv6/netfilter/nft_masq_ipv6.c |    1 +
 net/netfilter/ipset/ip_set_core.c  |    6 ++++++
 net/netfilter/ipvs/ip_vs_xmit.c    |    2 ++
 net/netfilter/nf_conntrack_core.c  |   14 +++++++------
 net/netfilter/nf_tables_api.c      |   24 ++++++++--------------
 net/netfilter/nft_compat.c         |   40 ++++++------------------------------
 8 files changed, 32 insertions(+), 58 deletions(-)
author maintainer
#2 lore raw

PATCH 1/8 netfilter: nft_masq: fix uninitialized range in nft_masq_{ipv4, ipv6}_eval

In reply to Pablo Neira Ayuso

From: Daniel Borkmann <[email protected]>

When transferring from the original range in nf_nat_masquerade_{ipv4,ipv6}() we copy over values from stack in from min_proto/max_proto due to uninitialized range variable in both, nft_masq_{ipv4,ipv6}_eval. As we only initialize flags at this time from nft_masq struct, just zero out the rest.

Fixes 9ba1f726bec09 ("netfilter: nf_tables: add new nft_masq expression")
---
 net/ipv4/netfilter/nft_masq_ipv4.c |    1 +
 net/ipv6/netfilter/nft_masq_ipv6.c |    1 +
 2 files changed, 2 insertions(+)
net/ipv4/netfilter/nft_masq_ipv4.c +1 -0
index c1023c4..665de06 100644
@@ -24,6 +24,7 @@ static void nft_masq_ipv4_eval(const struct nft_expr *expr,
 	struct nf_nat_range range;
 	unsigned int verdict;
 
+	memset(&range, 0, sizeof(range));
 	range.flags = priv->flags;
 
 	verdict = nf_nat_masquerade_ipv4(pkt->skb, pkt->ops->hooknum,
net/ipv6/netfilter/nft_masq_ipv6.c +1 -0
index 8a7ac68..529c119 100644
@@ -25,6 +25,7 @@ static void nft_masq_ipv6_eval(const struct nft_expr *expr,
 	struct nf_nat_range range;
 	unsigned int verdict;
 
+	memset(&range, 0, sizeof(range));
 	range.flags = priv->flags;
 
 	verdict = nf_nat_masquerade_ipv6(pkt->skb, &range, pkt->out);
Signature · 1 line
1.7.10.4
author maintainer
#3 lore raw

PATCH 2/8 netfilter: ipset: small potential read beyond the end of buffer

In reply to Pablo Neira Ayuso

From: Dan Carpenter <[email protected]>

We could be reading 8 bytes into a 4 byte buffer here. It seems harmless but adding a check is the right thing to do and it silences a static checker warning.

---
 net/netfilter/ipset/ip_set_core.c |    6 ++++++
 1 file changed, 6 insertions(+)
net/netfilter/ipset/ip_set_core.c +6 -0
index 86f9d76..d259da3 100644
@@ -1863,6 +1863,12 @@ ip_set_sockfn_get(struct sock *sk, int optval, void __user *user, int *len)
 	if (*op < IP_SET_OP_VERSION) {
 		/* Check the version at the beginning of operations */
 		struct ip_set_req_version *req_version = data;
+
+		if (*len < sizeof(struct ip_set_req_version)) {
+			ret = -EINVAL;
+			goto done;
+		}
+
 		if (req_version->version != IPSET_PROTOCOL) {
 			ret = -EPROTO;
 			goto done;
Signature · 1 line
1.7.10.4
author maintainer
#4 lore raw

PATCH 3/8 ipvs: Keep skb->sk when allocating headroom on tunnel xmit

In reply to Pablo Neira Ayuso

From: Calvin Owens <[email protected]>

ip_vs_prepare_tunneled_skb() ignores ->sk when allocating a new skb, either unconditionally setting ->sk to NULL or allowing the uninitialized ->sk from a newly allocated skb to leak through to the caller.

This patch properly copies ->sk and increments its reference count.

---
 net/netfilter/ipvs/ip_vs_xmit.c |    2 ++
 1 file changed, 2 insertions(+)
net/netfilter/ipvs/ip_vs_xmit.c +2 -0
index 437a366..bd90bf8 100644
@@ -846,6 +846,8 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
 		new_skb = skb_realloc_headroom(skb, max_headroom);
 		if (!new_skb)
 			goto error;
+		if (skb->sk)
+			skb_set_owner_w(new_skb, skb->sk);
 		consume_skb(skb);
 		skb = new_skb;
 	}
Signature · 1 line
1.7.10.4
author maintainer
#5 lore raw

PATCH 4/8 netfilter: nft_compat: use current net namespace

In reply to Pablo Neira Ayuso

Instead of init_net when using xtables over nftables compat.

---
 net/netfilter/nft_compat.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nft_compat.c +2 -2
index 9d6d6f6..b92f129 100644
@@ -117,7 +117,7 @@ nft_target_set_tgchk_param(struct xt_tgchk_param *par,
 			   struct xt_target *target, void *info,
 			   union nft_entry *entry, u8 proto, bool inv)
 {
-	par->net	= &init_net;
+	par->net	= ctx->net;
 	par->table	= ctx->table->name;
 	switch (ctx->afi->family) {
 	case AF_INET:
@@ -324,7 +324,7 @@ nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx,
 			  struct xt_match *match, void *info,
 			  union nft_entry *entry, u8 proto, bool inv)
 {
-	par->net	= &init_net;
+	par->net	= ctx->net;
 	par->table	= ctx->table->name;
 	switch (ctx->afi->family) {
 	case AF_INET:
Signature · 1 line
1.7.10.4
author maintainer
#6 lore raw

PATCH 5/8 netfilter: nft_compat: relax chain type validation

In reply to Pablo Neira Ayuso

Check for nat chain dependency only, which is the one that can actually crash the kernel. Don't care if mangle, filter and security specific match and targets are used out of their scope, they are harmless.

This restores iptables-compat with mangle specific match/target when used out of the OUTPUT chain, that are actually emulated through filter chains, which broke when performing strict validation.

Fixes f3f5dde ("netfilter: nft_compat: validate chain type in match/target")
---
 net/netfilter/nft_compat.c |   32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)
net/netfilter/nft_compat.c +2 -30
index b92f129..70dc965 100644
@@ -21,45 +21,17 @@
 #include <linux/netfilter_ipv6/ip6_tables.h>
 #include <net/netfilter/nf_tables.h>
 
-static const struct {
-       const char	*name;
-       u8		type;
-} table_to_chaintype[] = {
-       { "filter",     NFT_CHAIN_T_DEFAULT },
-       { "raw",        NFT_CHAIN_T_DEFAULT },
-       { "security",   NFT_CHAIN_T_DEFAULT },
-       { "mangle",     NFT_CHAIN_T_ROUTE },
-       { "nat",        NFT_CHAIN_T_NAT },
-       { },
-};
-
-static int nft_compat_table_to_chaintype(const char *table)
-{
-	int i;
-
-	for (i = 0; table_to_chaintype[i].name != NULL; i++) {
-		if (strcmp(table_to_chaintype[i].name, table) == 0)
-			return table_to_chaintype[i].type;
-	}
-
-	return -1;
-}
-
 static int nft_compat_chain_validate_dependency(const char *tablename,
 						const struct nft_chain *chain)
 {
-	enum nft_chain_type type;
 	const struct nft_base_chain *basechain;
 
 	if (!tablename || !(chain->flags & NFT_BASE_CHAIN))
 		return 0;
 
-	type = nft_compat_table_to_chaintype(tablename);
-	if (type < 0)
-		return -EINVAL;
-
 	basechain = nft_base_chain(chain);
-	if (basechain->type->type != type)
+	if (strcmp(tablename, "nat") == 0 &&
+	    basechain->type->type != NFT_CHAIN_T_NAT)
 		return -EINVAL;
 
 	return 0;
Signature · 1 line
1.7.10.4
author maintainer
#7 lore raw

PATCH 6/8 netfilter: nft_compat: use the match->table to validate dependencies

In reply to Pablo Neira Ayuso

Instead of the match->name, which is of course not relevant.

Fixes f3f5dde ("netfilter: nft_compat: validate chain type in match/target")
---
 net/netfilter/nft_compat.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
net/netfilter/nft_compat.c +2 -2
index 70dc965..265e190 100644
@@ -346,7 +346,7 @@ nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
 	union nft_entry e = {};
 	int ret;
 
-	ret = nft_compat_chain_validate_dependency(match->name, ctx->chain);
+	ret = nft_compat_chain_validate_dependency(match->table, ctx->chain);
 	if (ret < 0)
 		goto err;
 
@@ -420,7 +420,7 @@ static int nft_match_validate(const struct nft_ctx *ctx,
 		if (!(hook_mask & match->hooks))
 			return -EINVAL;
 
-		ret = nft_compat_chain_validate_dependency(match->name,
+		ret = nft_compat_chain_validate_dependency(match->table,
 							   ctx->chain);
 		if (ret < 0)
 			return ret;
Signature · 1 line
1.7.10.4
author maintainer
#8 lore raw

PATCH 7/8 netfilter: nf_tables: restore synchronous object release from commit/abort

In reply to Pablo Neira Ayuso

The existing xtables matches and targets, when used from nft_compat, may sleep from the destroy path, ie. when removing rules. Since the objects are released via call_rcu from softirq context, this results in lockdep splats and possible lockups that may be hard to reproduce.

Patrick also indicated that delayed object release via call_rcu can cause us problems in the ordering of event notifications when anonymous sets are in place.

So, this patch restores the synchronous object release from the commit and abort paths. This includes a call to synchronize_rcu() to make sure that no packets are walking on the objects that are going to be released. This is slowier though, but it's simple and it resolves the aforementioned problems.

This is a partial revert of c7c32e7 ("netfilter: nf_tables: defer all object release via rcu") that was introduced in 3.16 to speed up interaction with userspace.

---
 include/net/netfilter/nf_tables.h |    2 --
 net/netfilter/nf_tables_api.c     |   24 ++++++++----------------
 2 files changed, 8 insertions(+), 18 deletions(-)
include/net/netfilter/nf_tables.h +0 -2
index 845c596..3ae969e 100644
@@ -396,14 +396,12 @@ struct nft_rule {
 /**
  *	struct nft_trans - nf_tables object update in transaction
  *
- *	@rcu_head: rcu head to defer release of transaction data
  *	@list: used internally
  *	@msg_type: message type
  *	@ctx: transaction context
  *	@data: internal information related to the transaction
  */
 struct nft_trans {
-	struct rcu_head			rcu_head;
 	struct list_head		list;
 	int				msg_type;
 	struct nft_ctx			ctx;
net/netfilter/nf_tables_api.c +8 -16
index 11ab4b0..66e8425 100644
@@ -3484,13 +3484,8 @@ static void nft_chain_commit_update(struct nft_trans *trans)
 	}
 }
 
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * removed rules.
- */
-static void nf_tables_commit_release_rcu(struct rcu_head *rt)
+static void nf_tables_commit_release(struct nft_trans *trans)
 {
-	struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
 	switch (trans->msg_type) {
 	case NFT_MSG_DELTABLE:
 		nf_tables_table_destroy(&trans->ctx);
@@ -3612,10 +3607,11 @@ static int nf_tables_commit(struct sk_buff *skb)
 		}
 	}
 
+	synchronize_rcu();
+
 	list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
 		list_del(&trans->list);
-		trans->ctx.nla = NULL;
-		call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
+		nf_tables_commit_release(trans);
 	}
 
 	nf_tables_gen_notify(net, skb, NFT_MSG_NEWGEN);
@@ -3623,13 +3619,8 @@ static int nf_tables_commit(struct sk_buff *skb)
 	return 0;
 }
 
-/* Schedule objects for release via rcu to make sure no packets are accesing
- * aborted rules.
- */
-static void nf_tables_abort_release_rcu(struct rcu_head *rt)
+static void nf_tables_abort_release(struct nft_trans *trans)
 {
-	struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
-
 	switch (trans->msg_type) {
 	case NFT_MSG_NEWTABLE:
 		nf_tables_table_destroy(&trans->ctx);
@@ -3725,11 +3716,12 @@ static int nf_tables_abort(struct sk_buff *skb)
 		}
 	}
 
+	synchronize_rcu();
+
 	list_for_each_entry_safe_reverse(trans, next,
 					 &net->nft.commit_list, list) {
 		list_del(&trans->list);
-		trans->ctx.nla = NULL;
-		call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
+		nf_tables_abort_release(trans);
 	}
 
 	return 0;
Signature · 1 line
1.7.10.4
author maintainer
#9 lore raw

PATCH 8/8 netfilter: conntrack: fix race in __nf_conntrack_confirm against get_next_corpse

In reply to Pablo Neira Ayuso

From: bill bonaparte <[email protected]>

After removal of the central spinlock nf_conntrack_lock, in commit 93bb0ceb75be2 ("netfilter: conntrack: remove central spinlock nf_conntrack_lock"), it is possible to race against get_next_corpse().

The race is against the get_next_corpse() cleanup on the "unconfirmed" list (a per-cpu list with seperate locking), which set the DYING bit.

Fix this race, in __nf_conntrack_confirm(), by removing the CT from unconfirmed list before checking the DYING bit. In case race occured, re-add the CT to the dying list.

While at this, fix coding style of the comment that has been updated.

Fixes 93bb0ceb75be2 ("netfilter: conntrack: remove central spinlock nf_conntrack_lock")
---
 net/netfilter/nf_conntrack_core.c |   14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
net/netfilter/nf_conntrack_core.c +8 -6
index 5016a69..2c69975 100644
@@ -611,12 +611,16 @@ __nf_conntrack_confirm(struct sk_buff *skb)
 	 */
 	NF_CT_ASSERT(!nf_ct_is_confirmed(ct));
 	pr_debug("Confirming conntrack %p\n", ct);
-	/* We have to check the DYING flag inside the lock to prevent
-	   a race against nf_ct_get_next_corpse() possibly called from
-	   user context, else we insert an already 'dead' hash, blocking
-	   further use of that particular connection -JM */
+
+	/* We have to check the DYING flag after unlink to prevent
+	 * a race against nf_ct_get_next_corpse() possibly called from
+	 * user context, else we insert an already 'dead' hash, blocking
+	 * further use of that particular connection -JM.
+	 */
+	nf_ct_del_from_dying_or_unconfirmed_list(ct);
 
 	if (unlikely(nf_ct_is_dying(ct))) {
+		nf_ct_add_to_dying_list(ct);
 		nf_conntrack_double_unlock(hash, reply_hash);
 		local_bh_enable();
 		return NF_ACCEPT;
@@ -636,8 +640,6 @@ __nf_conntrack_confirm(struct sk_buff *skb)
 		    zone == nf_ct_zone(nf_ct_tuplehash_to_ctrack(h)))
 			goto out;
 
-	nf_ct_del_from_dying_or_unconfirmed_list(ct);
-
 	/* Timer relative to confirmation time, not original
 	   setting time, otherwise we'd get timer wrap in
 	   weird delay cases. */
Signature · 1 line
1.7.10.4
maintainer
#10 lore raw
In reply to Pablo Neira Ayuso

From: Pablo Neira Ayuso <[email protected]>
Date: Fri, 14 Nov 2014 17:58:40 +0100

Quoted 2 lines

The following patchset contains Netfilter updates for your net tree, they are:

...

Quoted 3 lines

You can pull these changes from:

git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf.git

Pulled, thanks a lot Pablo!