Re: [PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-13 Thread David Miller
From: Linus Lüssing 
Date: Thu, 13 Aug 2015 05:54:07 +0200

> The recent refactoring of the IGMP and MLD parsing code into
> ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
> BUG() invocation for bridges:
> 
> I wrongly assumed that skb_get() could be used as a simple reference
> counter for an skb which is not the case. skb_get() bears additional
> semantics, a user count. This leads to a BUG() invocation in
> pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
> with a user count greater than one - unfortunately the refactoring did
> just that.
> 
> Fixing this by removing the skb_get() call and changing the API: The
> caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
> additionally check whether the returned skb_trimmed is a clone.
> 
> Fixes: 9afd85c9e455 ("net: Export IGMP/MLD message validation code")
> Reported-by: Brenden Blanco 
> Signed-off-by: Linus Lüssing 

Applied, thanks.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-13 Thread David Miller
From: Linus Lüssing linus.luess...@c0d3.blue
Date: Thu, 13 Aug 2015 05:54:07 +0200

 The recent refactoring of the IGMP and MLD parsing code into
 ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
 BUG() invocation for bridges:
 
 I wrongly assumed that skb_get() could be used as a simple reference
 counter for an skb which is not the case. skb_get() bears additional
 semantics, a user count. This leads to a BUG() invocation in
 pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
 with a user count greater than one - unfortunately the refactoring did
 just that.
 
 Fixing this by removing the skb_get() call and changing the API: The
 caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
 additionally check whether the returned skb_trimmed is a clone.
 
 Fixes: 9afd85c9e455 (net: Export IGMP/MLD message validation code)
 Reported-by: Brenden Blanco bbla...@plumgrid.com
 Signed-off-by: Linus Lüssing linus.luess...@c0d3.blue

Applied, thanks.
--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


Re: [PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-12 Thread Alexei Starovoitov
On Thu, Aug 13, 2015 at 05:54:07AM +0200, Linus Lüssing wrote:
> The recent refactoring of the IGMP and MLD parsing code into
> ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
> BUG() invocation for bridges:
> 
> I wrongly assumed that skb_get() could be used as a simple reference
> counter for an skb which is not the case. skb_get() bears additional
> semantics, a user count. This leads to a BUG() invocation in
> pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
> with a user count greater than one - unfortunately the refactoring did
> just that.
> 
> Fixing this by removing the skb_get() call and changing the API: The
> caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
> additionally check whether the returned skb_trimmed is a clone.
> 
> Fixes: 9afd85c9e455 ("net: Export IGMP/MLD message validation code")
> Reported-by: Brenden Blanco 
> Signed-off-by: Linus Lüssing 

I think the fix actually made the code easier to read.
Thank you. Looks good to me.
Acked-by: Alexei Starovoitov 

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/


[PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-12 Thread Linus Lüssing
The recent refactoring of the IGMP and MLD parsing code into
ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
BUG() invocation for bridges:

I wrongly assumed that skb_get() could be used as a simple reference
counter for an skb which is not the case. skb_get() bears additional
semantics, a user count. This leads to a BUG() invocation in
pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
with a user count greater than one - unfortunately the refactoring did
just that.

Fixing this by removing the skb_get() call and changing the API: The
caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
additionally check whether the returned skb_trimmed is a clone.

Fixes: 9afd85c9e455 ("net: Export IGMP/MLD message validation code")
Reported-by: Brenden Blanco 
Signed-off-by: Linus Lüssing 
---
 net/bridge/br_multicast.c |4 ++--
 net/core/skbuff.c |   37 ++---
 net/ipv4/igmp.c   |   33 ++---
 net/ipv6/mcast_snoop.c|   33 ++---
 4 files changed, 56 insertions(+), 51 deletions(-)

diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 0b39dcc..1285eaf 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1591,7 +1591,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
break;
}
 
-   if (skb_trimmed)
+   if (skb_trimmed && skb_trimmed != skb)
kfree_skb(skb_trimmed);
 
return err;
@@ -1636,7 +1636,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
break;
}
 
-   if (skb_trimmed)
+   if (skb_trimmed && skb_trimmed != skb)
kfree_skb(skb_trimmed);
 
return err;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index b6a19ca..bf9a5d9 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4022,8 +4022,8 @@ EXPORT_SYMBOL(skb_checksum_setup);
  * Otherwise returns the provided skb. Returns NULL in error cases
  * (e.g. transport_len exceeds skb length or out-of-memory).
  *
- * Caller needs to set the skb transport header and release the returned skb.
- * Provided skb is consumed.
+ * Caller needs to set the skb transport header and free any returned skb if it
+ * differs from the provided skb.
  */
 static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb,
   unsigned int transport_len)
@@ -4032,16 +4032,12 @@ static struct sk_buff *skb_checksum_maybe_trim(struct 
sk_buff *skb,
unsigned int len = skb_transport_offset(skb) + transport_len;
int ret;
 
-   if (skb->len < len) {
-   kfree_skb(skb);
+   if (skb->len < len)
return NULL;
-   } else if (skb->len == len) {
+   else if (skb->len == len)
return skb;
-   }
 
skb_chk = skb_clone(skb, GFP_ATOMIC);
-   kfree_skb(skb);
-
if (!skb_chk)
return NULL;
 
@@ -4066,8 +4062,8 @@ static struct sk_buff *skb_checksum_maybe_trim(struct 
sk_buff *skb,
  * If the skb has data beyond the given transport length, then a
  * trimmed & cloned skb is checked and returned.
  *
- * Caller needs to set the skb transport header and release the returned skb.
- * Provided skb is consumed.
+ * Caller needs to set the skb transport header and free any returned skb if it
+ * differs from the provided skb.
  */
 struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
 unsigned int transport_len,
@@ -4079,23 +4075,26 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff 
*skb,
 
skb_chk = skb_checksum_maybe_trim(skb, transport_len);
if (!skb_chk)
-   return NULL;
+   goto err;
 
-   if (!pskb_may_pull(skb_chk, offset)) {
-   kfree_skb(skb_chk);
-   return NULL;
-   }
+   if (!pskb_may_pull(skb_chk, offset))
+   goto err;
 
__skb_pull(skb_chk, offset);
ret = skb_chkf(skb_chk);
__skb_push(skb_chk, offset);
 
-   if (ret) {
-   kfree_skb(skb_chk);
-   return NULL;
-   }
+   if (ret)
+   goto err;
 
return skb_chk;
+
+err:
+   if (skb_chk && skb_chk != skb)
+   kfree_skb(skb_chk);
+
+   return NULL;
+
 }
 EXPORT_SYMBOL(skb_checksum_trimmed);
 
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 651cdf6..9fdfd9d 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1435,33 +1435,35 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, 
struct sk_buff **skb_trimmed)
struct sk_buff *skb_chk;
unsigned int transport_len;
unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr);
-   int ret;
+   int ret = -EINVAL;
 
transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb);
 
-   skb_get(skb);
skb_chk = 

[PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-12 Thread Linus Lüssing
The recent refactoring of the IGMP and MLD parsing code into
ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
BUG() invocation for bridges:

I wrongly assumed that skb_get() could be used as a simple reference
counter for an skb which is not the case. skb_get() bears additional
semantics, a user count. This leads to a BUG() invocation in
pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
with a user count greater than one - unfortunately the refactoring did
just that.

Fixing this by removing the skb_get() call and changing the API: The
caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
additionally check whether the returned skb_trimmed is a clone.

Fixes: 9afd85c9e455 (net: Export IGMP/MLD message validation code)
Reported-by: Brenden Blanco bbla...@plumgrid.com
Signed-off-by: Linus Lüssing linus.luess...@c0d3.blue
---
 net/bridge/br_multicast.c |4 ++--
 net/core/skbuff.c |   37 ++---
 net/ipv4/igmp.c   |   33 ++---
 net/ipv6/mcast_snoop.c|   33 ++---
 4 files changed, 56 insertions(+), 51 deletions(-)

diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c
index 0b39dcc..1285eaf 100644
--- a/net/bridge/br_multicast.c
+++ b/net/bridge/br_multicast.c
@@ -1591,7 +1591,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br,
break;
}
 
-   if (skb_trimmed)
+   if (skb_trimmed  skb_trimmed != skb)
kfree_skb(skb_trimmed);
 
return err;
@@ -1636,7 +1636,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br,
break;
}
 
-   if (skb_trimmed)
+   if (skb_trimmed  skb_trimmed != skb)
kfree_skb(skb_trimmed);
 
return err;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index b6a19ca..bf9a5d9 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4022,8 +4022,8 @@ EXPORT_SYMBOL(skb_checksum_setup);
  * Otherwise returns the provided skb. Returns NULL in error cases
  * (e.g. transport_len exceeds skb length or out-of-memory).
  *
- * Caller needs to set the skb transport header and release the returned skb.
- * Provided skb is consumed.
+ * Caller needs to set the skb transport header and free any returned skb if it
+ * differs from the provided skb.
  */
 static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb,
   unsigned int transport_len)
@@ -4032,16 +4032,12 @@ static struct sk_buff *skb_checksum_maybe_trim(struct 
sk_buff *skb,
unsigned int len = skb_transport_offset(skb) + transport_len;
int ret;
 
-   if (skb-len  len) {
-   kfree_skb(skb);
+   if (skb-len  len)
return NULL;
-   } else if (skb-len == len) {
+   else if (skb-len == len)
return skb;
-   }
 
skb_chk = skb_clone(skb, GFP_ATOMIC);
-   kfree_skb(skb);
-
if (!skb_chk)
return NULL;
 
@@ -4066,8 +4062,8 @@ static struct sk_buff *skb_checksum_maybe_trim(struct 
sk_buff *skb,
  * If the skb has data beyond the given transport length, then a
  * trimmed  cloned skb is checked and returned.
  *
- * Caller needs to set the skb transport header and release the returned skb.
- * Provided skb is consumed.
+ * Caller needs to set the skb transport header and free any returned skb if it
+ * differs from the provided skb.
  */
 struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb,
 unsigned int transport_len,
@@ -4079,23 +4075,26 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff 
*skb,
 
skb_chk = skb_checksum_maybe_trim(skb, transport_len);
if (!skb_chk)
-   return NULL;
+   goto err;
 
-   if (!pskb_may_pull(skb_chk, offset)) {
-   kfree_skb(skb_chk);
-   return NULL;
-   }
+   if (!pskb_may_pull(skb_chk, offset))
+   goto err;
 
__skb_pull(skb_chk, offset);
ret = skb_chkf(skb_chk);
__skb_push(skb_chk, offset);
 
-   if (ret) {
-   kfree_skb(skb_chk);
-   return NULL;
-   }
+   if (ret)
+   goto err;
 
return skb_chk;
+
+err:
+   if (skb_chk  skb_chk != skb)
+   kfree_skb(skb_chk);
+
+   return NULL;
+
 }
 EXPORT_SYMBOL(skb_checksum_trimmed);
 
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 651cdf6..9fdfd9d 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -1435,33 +1435,35 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, 
struct sk_buff **skb_trimmed)
struct sk_buff *skb_chk;
unsigned int transport_len;
unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr);
-   int ret;
+   int ret = -EINVAL;
 
transport_len = ntohs(ip_hdr(skb)-tot_len) - ip_hdrlen(skb);
 
-   skb_get(skb);

Re: [PATCH] net: fix wrong skb_get() usage / crash in IGMP/MLD parsing code

2015-08-12 Thread Alexei Starovoitov
On Thu, Aug 13, 2015 at 05:54:07AM +0200, Linus Lüssing wrote:
 The recent refactoring of the IGMP and MLD parsing code into
 ipv6_mc_check_mld() / ip_mc_check_igmp() introduced a potential crash /
 BUG() invocation for bridges:
 
 I wrongly assumed that skb_get() could be used as a simple reference
 counter for an skb which is not the case. skb_get() bears additional
 semantics, a user count. This leads to a BUG() invocation in
 pskb_expand_head() / kernel panic if pskb_may_pull() is called on an skb
 with a user count greater than one - unfortunately the refactoring did
 just that.
 
 Fixing this by removing the skb_get() call and changing the API: The
 caller of ipv6_mc_check_mld() / ip_mc_check_igmp() now needs to
 additionally check whether the returned skb_trimmed is a clone.
 
 Fixes: 9afd85c9e455 (net: Export IGMP/MLD message validation code)
 Reported-by: Brenden Blanco bbla...@plumgrid.com
 Signed-off-by: Linus Lüssing linus.luess...@c0d3.blue

I think the fix actually made the code easier to read.
Thank you. Looks good to me.
Acked-by: Alexei Starovoitov a...@plumgrid.com

--
To unsubscribe from this list: send the line unsubscribe linux-kernel in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/