diff mbox series

ip6_gre: fix ip6gre_err() invalid reads

Message ID 1532359863-18441-2-git-send-email-paolo.pisati@canonical.com
State New
Headers show
Series ip6_gre: fix ip6gre_err() invalid reads | expand

Commit Message

Paolo Pisati July 23, 2018, 3:31 p.m. UTC
From: Eric Dumazet <edumazet@google.com>

CVE-2017-5897

Andrey Konovalov reported out of bound accesses in ip6gre_err()

If GRE flags contains GRE_KEY, the following expression
*(((__be32 *)p) + (grehlen / 4) - 1)

accesses data ~40 bytes after the expected point, since
grehlen includes the size of IPv6 headers.

Let's use a "struct gre_base_hdr *greh" pointer to make this
code more readable.

p[1] becomes greh->protocol.
grhlen is the GRE header length.

Fixes: c12b395a4664 ("gre: Support GRE over IPv6")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reported-by: Andrey Konovalov <andreyknvl@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit 7892032cfe67f4bde6fc2ee967e45a8fbaf33756)
Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
---
 net/ipv6/ip6_gre.c | 41 ++++++++++++++++++++++-------------------
 1 file changed, 22 insertions(+), 19 deletions(-)

Comments

Stefan Bader July 24, 2018, 12:40 p.m. UTC | #1
On 23.07.2018 17:31, Paolo Pisati wrote:
> From: Eric Dumazet <edumazet@google.com>
> 
> CVE-2017-5897
> 
> Andrey Konovalov reported out of bound accesses in ip6gre_err()
> 
> If GRE flags contains GRE_KEY, the following expression
> *(((__be32 *)p) + (grehlen / 4) - 1)
> 
> accesses data ~40 bytes after the expected point, since
> grehlen includes the size of IPv6 headers.
> 
> Let's use a "struct gre_base_hdr *greh" pointer to make this
> code more readable.
> 
> p[1] becomes greh->protocol.
> grhlen is the GRE header length.
> 
> Fixes: c12b395a4664 ("gre: Support GRE over IPv6")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> Reported-by: Andrey Konovalov <andreyknvl@google.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
> (cherry picked from commit 7892032cfe67f4bde6fc2ee967e45a8fbaf33756)
> Signed-off-by: Paolo Pisati <paolo.pisati@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---
>  net/ipv6/ip6_gre.c | 41 ++++++++++++++++++++++-------------------
>  1 file changed, 22 insertions(+), 19 deletions(-)
> 
> diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
> index 7075205..11feae0 100644
> --- a/net/ipv6/ip6_gre.c
> +++ b/net/ipv6/ip6_gre.c
> @@ -56,6 +56,7 @@
>  #include <net/ip6_route.h>
>  #include <net/ip6_tunnel.h>
>  
> +#include <net/gre.h>
>  
>  static bool log_ecn_error = true;
>  module_param(log_ecn_error, bool, 0644);
> @@ -366,35 +367,37 @@ static void ip6gre_tunnel_uninit(struct net_device *dev)
>  
>  
>  static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
> -		u8 type, u8 code, int offset, __be32 info)
> +		       u8 type, u8 code, int offset, __be32 info)
>  {
> -	const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
> -	__be16 *p = (__be16 *)(skb->data + offset);
> -	int grehlen = offset + 4;
> +	const struct gre_base_hdr *greh;
> +	const struct ipv6hdr *ipv6h;
> +	int grehlen = sizeof(*greh);
>  	struct ip6_tnl *t;
> +	int key_off = 0;
>  	__be16 flags;
> +	__be32 key;
>  
> -	flags = p[0];
> -	if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
> -		if (flags&(GRE_VERSION|GRE_ROUTING))
> -			return;
> -		if (flags&GRE_KEY) {
> -			grehlen += 4;
> -			if (flags&GRE_CSUM)
> -				grehlen += 4;
> -		}
> +	if (!pskb_may_pull(skb, offset + grehlen))
> +		return;
> +	greh = (const struct gre_base_hdr *)(skb->data + offset);
> +	flags = greh->flags;
> +	if (flags & (GRE_VERSION | GRE_ROUTING))
> +		return;
> +	if (flags & GRE_CSUM)
> +		grehlen += 4;
> +	if (flags & GRE_KEY) {
> +		key_off = grehlen + offset;
> +		grehlen += 4;
>  	}
>  
> -	/* If only 8 bytes returned, keyed message will be dropped here */
> -	if (!pskb_may_pull(skb, grehlen))
> +	if (!pskb_may_pull(skb, offset + grehlen))
>  		return;
>  	ipv6h = (const struct ipv6hdr *)skb->data;
> -	p = (__be16 *)(skb->data + offset);
> +	greh = (const struct gre_base_hdr *)(skb->data + offset);
> +	key = key_off ? *(__be32 *)(skb->data + key_off) : 0;
>  
>  	t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
> -				flags & GRE_KEY ?
> -				*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
> -				p[1]);
> +				 key, greh->protocol);
>  	if (t == NULL)
>  		return;
>  
>
diff mbox series

Patch

diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index 7075205..11feae0 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -56,6 +56,7 @@ 
 #include <net/ip6_route.h>
 #include <net/ip6_tunnel.h>
 
+#include <net/gre.h>
 
 static bool log_ecn_error = true;
 module_param(log_ecn_error, bool, 0644);
@@ -366,35 +367,37 @@  static void ip6gre_tunnel_uninit(struct net_device *dev)
 
 
 static void ip6gre_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
-		u8 type, u8 code, int offset, __be32 info)
+		       u8 type, u8 code, int offset, __be32 info)
 {
-	const struct ipv6hdr *ipv6h = (const struct ipv6hdr *)skb->data;
-	__be16 *p = (__be16 *)(skb->data + offset);
-	int grehlen = offset + 4;
+	const struct gre_base_hdr *greh;
+	const struct ipv6hdr *ipv6h;
+	int grehlen = sizeof(*greh);
 	struct ip6_tnl *t;
+	int key_off = 0;
 	__be16 flags;
+	__be32 key;
 
-	flags = p[0];
-	if (flags&(GRE_CSUM|GRE_KEY|GRE_SEQ|GRE_ROUTING|GRE_VERSION)) {
-		if (flags&(GRE_VERSION|GRE_ROUTING))
-			return;
-		if (flags&GRE_KEY) {
-			grehlen += 4;
-			if (flags&GRE_CSUM)
-				grehlen += 4;
-		}
+	if (!pskb_may_pull(skb, offset + grehlen))
+		return;
+	greh = (const struct gre_base_hdr *)(skb->data + offset);
+	flags = greh->flags;
+	if (flags & (GRE_VERSION | GRE_ROUTING))
+		return;
+	if (flags & GRE_CSUM)
+		grehlen += 4;
+	if (flags & GRE_KEY) {
+		key_off = grehlen + offset;
+		grehlen += 4;
 	}
 
-	/* If only 8 bytes returned, keyed message will be dropped here */
-	if (!pskb_may_pull(skb, grehlen))
+	if (!pskb_may_pull(skb, offset + grehlen))
 		return;
 	ipv6h = (const struct ipv6hdr *)skb->data;
-	p = (__be16 *)(skb->data + offset);
+	greh = (const struct gre_base_hdr *)(skb->data + offset);
+	key = key_off ? *(__be32 *)(skb->data + key_off) : 0;
 
 	t = ip6gre_tunnel_lookup(skb->dev, &ipv6h->daddr, &ipv6h->saddr,
-				flags & GRE_KEY ?
-				*(((__be32 *)p) + (grehlen / 4) - 1) : 0,
-				p[1]);
+				 key, greh->protocol);
 	if (t == NULL)
 		return;