Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 3221

kernel-2.6.18-238.el5.src.rpm

From: Michal Schmidt <mschmidt@redhat.com>
Date: Fri, 3 Sep 2010 19:57:46 -0400
Subject: [net] vlan/bridge: fix skb_pull_rcsum fatal exception
Message-id: <20100903195744.7750.82062.stgit@localhost6.localdomain6>
Patchwork-id: 28127
O-Subject: [RHEL5.6 BZ556476 PATCH 1/6] vlan/bridge: Fix "skb_pull_rcsum - Fatal
	exception in interrupt"
Bugzilla: 556476
RH-Acked-by: David S. Miller <davem@redhat.com>
RH-Acked-by: Thomas Graf <tgraf@redhat.com>

From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>

commit e7c243c925f6d9dcb898504ff24d6650b5cbb3b1 upstream.

 I tried to preserve bridging code as it was before, but logic is quite
 strange - I think we should free skb on error, since it is already
 unshared and thus will just leak.

 Herbert Xu states:

 > +	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
 > +		goto out;

 If this happens it'll be a double-free on skb since we'll
 return NF_DROP which makes the caller free it too.

 We could return NF_STOLEN to prevent that but I'm not sure
 whether that's correct netfilter semantics.  Patrick, could
 you please make a call on this?

 Patrick McHardy states:

 NF_STOLEN should work fine here.

 Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
 Signed-off-by: David S. Miller <davem@davemloft.net>
[bwh: Backport to RHEL 5]

diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 158a2ae..366816d 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -117,12 +117,22 @@ int vlan_skb_recv(struct sk_buff *skb, struct net_device *dev,
                   struct packet_type* ptype, struct net_device *orig_dev)
 {
 	unsigned char *rawp = NULL;
-	struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data);
+	struct vlan_hdr *vhdr;
 	unsigned short vid;
 	struct net_device_stats *stats;
 	unsigned short vlan_TCI;
 	__be16 proto;
 
+	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+		return -1;
+
+	if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) {
+		kfree_skb(skb);
+		return -1;
+	}
+
+	vhdr = (struct vlan_hdr *)(skb->data);
+
 	/* vlan_TCI = ntohs(get_unaligned(&vhdr->h_vlan_TCI)); */
 	vlan_TCI = ntohs(vhdr->h_vlan_TCI);
 
diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c
index 05b3de8..bad0e08 100644
--- a/net/bridge/br_netfilter.c
+++ b/net/bridge/br_netfilter.c
@@ -446,13 +446,18 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
 	__u32 len;
 	struct sk_buff *skb = *pskb;
 
+	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)
+		return NF_STOLEN;
+
+	if (skb->protocol == htons(ETH_P_8021Q) &&
+	    unlikely(!pskb_may_pull(skb, VLAN_HLEN)))
+		goto out;
+
 	if (skb->protocol == htons(ETH_P_IPV6) || IS_VLAN_IPV6(skb)) {
 #ifdef CONFIG_SYSCTL
 		if (!brnf_call_ip6tables)
 			return NF_ACCEPT;
 #endif
-		if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
-			goto out;
 
 		if (skb->protocol == htons(ETH_P_8021Q)) {
 			skb_pull_rcsum(skb, VLAN_HLEN);
@@ -468,9 +473,6 @@ static unsigned int br_nf_pre_routing(unsigned int hook, struct sk_buff **pskb,
 	if (skb->protocol != htons(ETH_P_IP) && !IS_VLAN_IP(skb))
 		return NF_ACCEPT;
 
-	if ((skb = skb_share_check(*pskb, GFP_ATOMIC)) == NULL)
-		goto out;
-
 	if (skb->protocol == htons(ETH_P_8021Q)) {
 		skb_pull_rcsum(skb, VLAN_HLEN);
 		skb->nh.raw += VLAN_HLEN;