Skip to contentSkip to conversation

pull request (net): ipsec 2018-10-01

PATCH In review Steffen Klassert 3 participants 9 messages Oct 1, 2018 – Oct 2, 2018

Subsystems (3)
lore
Share & tools
lore

Steffen Klassert

author maintainer
#0 lore raw

1) Validate address prefix lengths in the xfrm selector, otherwise we may hit undefined behaviour in the address matching functions if the prefix is too big for the given address family.

2) Fix skb leak on local message size errors.
From Thadeu Lima de Souza Cascardo.

3) We currently reset the transport header back to the network header after a transport mode transformation is applied. This leads to an incorrect transport header when multiple transport mode transformations are applied. Reset the transport header only after all transformations are already applied to fix this. From Sowmini Varadhan.

4) We only support one offloaded xfrm, so reset crypto_done after the first transformation in xfrm_input(). Otherwise we may call the wrong input method for subsequent transformations. From Sowmini Varadhan.

5) Fix NULL pointer dereference when skb_dst_force clears the dst_entry. skb_dst_force does not really force a dst refcount anymore, it might clear it instead. xfrm code did not expect this, add a check to not dereference skb_dst() if it was cleared by skb_dst_force.

6) Validate xfrm template mode, otherwise we can get a stack-out-of-bounds read in xfrm_state_find. From Sean Tranchetti.

Please pull or let me know if there are problems.

Thanks!

The following changes since commit 25432eba9cd8f2ef5afef55be811b010a004b5fa:

openvswitch: meter: Fix setting meter id for new entries (2018-07-29 13:20:54 -0700)

are available in the Git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/klassert/ipsec.git master

for you to fetch changes up to 32bf94fb5c2ec4ec842152d0e5937cd4bb6738fa:

xfrm: validate template mode (2018-09-20 08:30:42 +0200)

----------------------------------------------------------------
Sean Tranchetti (1):
xfrm: validate template mode

Sowmini Varadhan (2):
xfrm: reset transport header back to network header after all input transforms ahave been applied
xfrm: reset crypto_done when iterating over multiple input xfrms

Steffen Klassert (2):
xfrm: Validate address prefix lengths in the xfrm selector.
xfrm: Fix NULL pointer dereference when skb_dst_force clears the dst_entry.

Thadeu Lima de Souza Cascardo (1):
xfrm6: call kfree_skb when skb is toobig

 net/ipv4/xfrm4_input.c          |  1 +
 net/ipv4/xfrm4_mode_transport.c |  4 +---
 net/ipv6/xfrm6_input.c          |  1 +
 net/ipv6/xfrm6_mode_transport.c |  4 +---
 net/ipv6/xfrm6_output.c         |  2 ++
 net/xfrm/xfrm_input.c           |  1 +
 net/xfrm/xfrm_output.c          |  4 ++++
 net/xfrm/xfrm_policy.c          |  4 ++++
 net/xfrm/xfrm_user.c            | 15 +++++++++++++++
 9 files changed, 30 insertions(+), 6 deletions(-)
author maintainer
#1 lore raw

PATCH 1/6 xfrm: Validate address prefix lengths in the xfrm selector.

We don't validate the address prefix lengths in the xfrm selector we got from userspace. This can lead to undefined behaviour in the address matching functions if the prefix is too big for the given address family. Fix this by checking the prefixes and refuse SA/policy insertation when a prefix is invalid.

Fixes 1da177e4c3f4 ("Linux-2.6.12-rc2")
---
 net/xfrm/xfrm_user.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
net/xfrm/xfrm_user.c +12 -0
index 33878e6e0d0a..5151b3ebf068 100644
@@ -151,10 +151,16 @@ static int verify_newsa_info(struct xfrm_usersa_info *p,
 	err = -EINVAL;
 	switch (p->family) {
 	case AF_INET:
+		if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32)
+			goto out;
+
 		break;
 
 	case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
+		if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128)
+			goto out;
+
 		break;
 #else
 		err = -EAFNOSUPPORT;
@@ -1359,10 +1365,16 @@ static int verify_newpolicy_info(struct xfrm_userpolicy_info *p)
 
 	switch (p->sel.family) {
 	case AF_INET:
+		if (p->sel.prefixlen_d > 32 || p->sel.prefixlen_s > 32)
+			return -EINVAL;
+
 		break;
 
 	case AF_INET6:
 #if IS_ENABLED(CONFIG_IPV6)
+		if (p->sel.prefixlen_d > 128 || p->sel.prefixlen_s > 128)
+			return -EINVAL;
+
 		break;
 #else
 		return  -EAFNOSUPPORT;
Signature · 1 line
2.17.1
author maintainer
#2 lore raw

PATCH 2/6 xfrm6: call kfree_skb when skb is toobig

In reply to Steffen Klassert

From: Thadeu Lima de Souza Cascardo <[email protected]>

After commit d6990976af7c5d8f55903bfb4289b6fb030bf754 ("vti6: fix PMTU caching and reporting on xmit"), some too big skbs might be potentially passed down to __xfrm6_output, causing it to fail to transmit but not free the skb, causing a leak of skb, and consequentially a leak of dst references.

After running pmtu.sh, that shows as failure to unregister devices in a namespace:

[  311.397671] unregister_netdevice: waiting for veth_b to become free. Usage count = 1

The fix is to call kfree_skb in case of transmit failures.

Fixes dd767856a36e ("xfrm6: Don't call icmpv6_send on local error")
---
 net/ipv6/xfrm6_output.c | 2 ++
 1 file changed, 2 insertions(+)
net/ipv6/xfrm6_output.c +2 -0
index 5959ce9620eb..6a74080005cf 100644
@@ -170,9 +170,11 @@ static int __xfrm6_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 
 	if (toobig && xfrm6_local_dontfrag(skb)) {
 		xfrm6_local_rxpmtu(skb, mtu);
+		kfree_skb(skb);
 		return -EMSGSIZE;
 	} else if (!skb->ignore_df && toobig && skb->sk) {
 		xfrm_local_error(skb, mtu);
+		kfree_skb(skb);
 		return -EMSGSIZE;
 	}
 
Signature · 1 line
2.17.1
author maintainer
#3 lore raw

PATCH 3/6 xfrm: reset transport header back to network header after all input transforms ahave been applied

In reply to Steffen Klassert

From: Sowmini Varadhan <[email protected]>

A policy may have been set up with multiple transforms (e.g., ESP and ipcomp). In this situation, the ingress IPsec processing iterates in xfrm_input() and applies each transform in turn, processing the nexthdr to find any additional xfrm that may apply.

This patch resets the transport header back to network header only after the last transformation so that subsequent xfrms can find the correct transport header.

Fixes 7785bba299a8 ("esp: Add a software GRO codepath")
---
 net/ipv4/xfrm4_input.c          | 1 +
 net/ipv4/xfrm4_mode_transport.c | 4 +---
 net/ipv6/xfrm6_input.c          | 1 +
 net/ipv6/xfrm6_mode_transport.c | 4 +---
 4 files changed, 4 insertions(+), 6 deletions(-)
net/ipv4/xfrm4_input.c +1 -0
index bcfc00e88756..f8de2482a529 100644
@@ -67,6 +67,7 @@ int xfrm4_transport_finish(struct sk_buff *skb, int async)
 
 	if (xo && (xo->flags & XFRM_GRO)) {
 		skb_mac_header_rebuild(skb);
+		skb_reset_transport_header(skb);
 		return 0;
 	}
 
net/ipv4/xfrm4_mode_transport.c +1 -3
index 3d36644890bb..1ad2c2c4e250 100644
@@ -46,7 +46,6 @@ static int xfrm4_transport_output(struct xfrm_state *x, struct sk_buff *skb)
 static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	int ihl = skb->data - skb_transport_header(skb);
-	struct xfrm_offload *xo = xfrm_offload(skb);
 
 	if (skb->transport_header != skb->network_header) {
 		memmove(skb_transport_header(skb),
@@ -54,8 +53,7 @@ static int xfrm4_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 		skb->network_header = skb->transport_header;
 	}
 	ip_hdr(skb)->tot_len = htons(skb->len + ihl);
-	if (!xo || !(xo->flags & XFRM_GRO))
-		skb_reset_transport_header(skb);
+	skb_reset_transport_header(skb);
 	return 0;
 }
 
net/ipv6/xfrm6_input.c +1 -0
index 841f4a07438e..9ef490dddcea 100644
@@ -59,6 +59,7 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async)
 
 	if (xo && (xo->flags & XFRM_GRO)) {
 		skb_mac_header_rebuild(skb);
+		skb_reset_transport_header(skb);
 		return -1;
 	}
 
net/ipv6/xfrm6_mode_transport.c +1 -3
index 9ad07a91708e..3c29da5defe6 100644
@@ -51,7 +51,6 @@ static int xfrm6_transport_output(struct xfrm_state *x, struct sk_buff *skb)
 static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 {
 	int ihl = skb->data - skb_transport_header(skb);
-	struct xfrm_offload *xo = xfrm_offload(skb);
 
 	if (skb->transport_header != skb->network_header) {
 		memmove(skb_transport_header(skb),
@@ -60,8 +59,7 @@ static int xfrm6_transport_input(struct xfrm_state *x, struct sk_buff *skb)
 	}
 	ipv6_hdr(skb)->payload_len = htons(skb->len + ihl -
 					   sizeof(struct ipv6hdr));
-	if (!xo || !(xo->flags & XFRM_GRO))
-		skb_reset_transport_header(skb);
+	skb_reset_transport_header(skb);
 	return 0;
 }
 
Signature · 1 line
2.17.1
author maintainer
#4 lore raw

PATCH 4/6 xfrm: reset crypto_done when iterating over multiple input xfrms

In reply to Steffen Klassert

From: Sowmini Varadhan <[email protected]>

We only support one offloaded xfrm (we do not have devices that can handle more than one offload), so reset crypto_done in xfrm_input() when iterating over multiple transforms in xfrm_input, so that we can invoke the appropriate x->type->input for the non-offloaded transforms

Fixes d77e38e612a0 ("xfrm: Add an IPsec hardware offloading API")
---
 net/xfrm/xfrm_input.c | 1 +
 1 file changed, 1 insertion(+)
net/xfrm/xfrm_input.c +1 -0
index 352abca2605f..86f5afbd0a0c 100644
@@ -453,6 +453,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type)
 			XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR);
 			goto drop;
 		}
+		crypto_done = false;
 	} while (!err);
 
 	err = xfrm_rcv_cb(skb, family, x->type->proto, 0);
Signature · 1 line
2.17.1
author maintainer
#5 lore raw

PATCH 5/6 xfrm: Fix NULL pointer dereference when skb_dst_force clears the dst_entry.

In reply to Steffen Klassert

Since commit 222d7dbd258d ("net: prevent dst uses after free") skb_dst_force() might clear the dst_entry attached to the skb. The xfrm code don't expect this to happen, so we crash with a NULL pointer dereference in this case. Fix it by checking skb_dst(skb) for NULL after skb_dst_force() and drop the packet in cast the dst_entry was cleared.

Fixes 222d7dbd258d ("net: prevent dst uses after free")
---
 net/xfrm/xfrm_output.c | 4 ++++
 net/xfrm/xfrm_policy.c | 4 ++++
 2 files changed, 8 insertions(+)
net/xfrm/xfrm_output.c +4 -0
index 89b178a78dc7..36d15a38ce5e 100644
@@ -101,6 +101,10 @@ static int xfrm_output_one(struct sk_buff *skb, int err)
 		spin_unlock_bh(&x->lock);
 
 		skb_dst_force(skb);
+		if (!skb_dst(skb)) {
+			XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTERROR);
+			goto error_nolock;
+		}
 
 		if (xfrm_offload(skb)) {
 			x->type_offload->encap(x, skb);
net/xfrm/xfrm_policy.c +4 -0
index 7c5e8978aeaa..626e0f4d1749 100644
@@ -2548,6 +2548,10 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family)
 	}
 
 	skb_dst_force(skb);
+	if (!skb_dst(skb)) {
+		XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR);
+		return 0;
+	}
 
 	dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, XFRM_LOOKUP_QUEUE);
 	if (IS_ERR(dst)) {
Signature · 1 line
2.17.1
#6 lore raw

Re: [PATCH 5/6] xfrm: Fix NULL pointer dereference when skb_dst_force clears the dst_entry.

Quoted 3 lines · Steffen Klassert

Since commit 222d7dbd258d ("net: prevent dst uses after free") skb_dst_force() might clear the dst_entry attached to the skb. The xfrm code don't expect this to happen, so we crash with

Doesn't.

Quoted 3 lines

a NULL pointer dereference in this case. Fix it by checking skb_dst(skb) for NULL after skb_dst_force() and drop the packet in cast the dst_entry was cleared.

In case?

Quoted 5 lines
Fixes 222d7dbd258d ("net: prevent dst uses after free")

[...]

MBR, Sergei

author maintainer
#7 lore raw

PATCH 6/6 xfrm: validate template mode

In reply to Steffen Klassert

From: Sean Tranchetti <[email protected]>

XFRM mode parameters passed as part of the user templates in the IP_XFRM_POLICY are never properly validated. Passing values other than valid XFRM modes can cause stack-out-of-bounds reads to occur later in the XFRM processing:

[  140.535608] ================================================================
[  140.543058] BUG: KASAN: stack-out-of-bounds in xfrm_state_find+0x17e4/0x1cc4
[  140.550306] Read of size 4 at addr ffffffc0238a7a58 by task repro/5148
[  140.557369]
[  140.558927] Call trace:
[  140.558936] dump_backtrace+0x0/0x388
[  140.558940] show_stack+0x24/0x30
[  140.558946] __dump_stack+0x24/0x2c
[  140.558949] dump_stack+0x8c/0xd0
[  140.558956] print_address_description+0x74/0x234
[  140.558960] kasan_report+0x240/0x264
[  140.558963] __asan_report_load4_noabort+0x2c/0x38
[  140.558967] xfrm_state_find+0x17e4/0x1cc4
[  140.558971] xfrm_resolve_and_create_bundle+0x40c/0x1fb8
[  140.558975] xfrm_lookup+0x238/0x1444
[  140.558977] xfrm_lookup_route+0x48/0x11c
[  140.558984] ip_route_output_flow+0x88/0xc4
[  140.558991] raw_sendmsg+0xa74/0x266c
[  140.558996] inet_sendmsg+0x258/0x3b0
[  140.559002] sock_sendmsg+0xbc/0xec
[  140.559005] SyS_sendto+0x3a8/0x5a8
[  140.559008] el0_svc_naked+0x34/0x38
[  140.559009]
[  140.592245] page dumped because: kasan: bad access detected
[  140.597981] page_owner info is not active (free page?)
[  140.603267]
[  140.653503] ================================================================
---
 net/xfrm/xfrm_user.c | 3 +++
 1 file changed, 3 insertions(+)
net/xfrm/xfrm_user.c +3 -0
index 5151b3ebf068..d0672c400c2f 100644
@@ -1455,6 +1455,9 @@ static int validate_tmpl(int nr, struct xfrm_user_tmpl *ut, u16 family)
 		    (ut[i].family != prev_family))
 			return -EINVAL;
 
+		if (ut[i].mode >= XFRM_MODE_MAX)
+			return -EINVAL;
+
 		prev_family = ut[i].family;
 
 		switch (ut[i].family) {
Signature · 1 line
2.17.1
maintainer
#8 lore raw
In reply to Steffen Klassert

From: Steffen Klassert <[email protected]>
Date: Mon, 1 Oct 2018 10:58:49 +0200

Quoted 25 lines

1) Validate address prefix lengths in the xfrm selector, otherwise we may hit undefined behaviour in the address matching functions if the prefix is too big for the given address family.

2) Fix skb leak on local message size errors.
From Thadeu Lima de Souza Cascardo.

3) We currently reset the transport header back to the network header after a transport mode transformation is applied. This leads to an incorrect transport header when multiple transport mode transformations are applied. Reset the transport header only after all transformations are already applied to fix this. From Sowmini Varadhan.

4) We only support one offloaded xfrm, so reset crypto_done after the first transformation in xfrm_input(). Otherwise we may call the wrong input method for subsequent transformations. From Sowmini Varadhan.

5) Fix NULL pointer dereference when skb_dst_force clears the dst_entry. skb_dst_force does not really force a dst refcount anymore, it might clear it instead. xfrm code did not expect this, add a check to not dereference skb_dst() if it was cleared by skb_dst_force.

Quoted 4 lines

6) Validate xfrm template mode, otherwise we can get a stack-out-of-bounds read in xfrm_state_find. From Sean Tranchetti.

Please pull or let me know if there are problems.

Pulled, thanks!