diff options
author | xeb@mail.ru <xeb@mail.ru> | 2011-07-22 16:49:40 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-07-23 23:06:00 -0400 |
commit | 559fafb94ad9e4cd8774f39241917c57396f9fc5 (patch) | |
tree | f1c255fff42f8429ba3d17f4a7e19f1cc8cfe338 /net | |
parent | b0fe4a31849063fcac0bdc93716ca92615e93f57 (diff) |
gre: fix improper error handling
Fix improper protocol err_handler, current implementation is fully
unapplicable and may cause kernel crash due to double kfree_skb.
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/gre.c | 21 |
1 files changed, 6 insertions, 15 deletions
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c index 9dbe10875fbd..dbfc21de3479 100644 --- a/net/ipv4/gre.c +++ b/net/ipv4/gre.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/kmod.h> | 15 | #include <linux/kmod.h> |
16 | #include <linux/skbuff.h> | 16 | #include <linux/skbuff.h> |
17 | #include <linux/in.h> | 17 | #include <linux/in.h> |
18 | #include <linux/ip.h> | ||
18 | #include <linux/netdevice.h> | 19 | #include <linux/netdevice.h> |
19 | #include <linux/spinlock.h> | 20 | #include <linux/spinlock.h> |
20 | #include <net/protocol.h> | 21 | #include <net/protocol.h> |
@@ -96,27 +97,17 @@ drop: | |||
96 | static void gre_err(struct sk_buff *skb, u32 info) | 97 | static void gre_err(struct sk_buff *skb, u32 info) |
97 | { | 98 | { |
98 | const struct gre_protocol *proto; | 99 | const struct gre_protocol *proto; |
99 | u8 ver; | 100 | const struct iphdr *iph = (const struct iphdr *)skb->data; |
100 | 101 | u8 ver = skb->data[(iph->ihl<<2) + 1]&0x7f; | |
101 | if (!pskb_may_pull(skb, 12)) | ||
102 | goto drop; | ||
103 | 102 | ||
104 | ver = skb->data[1]&0x7f; | ||
105 | if (ver >= GREPROTO_MAX) | 103 | if (ver >= GREPROTO_MAX) |
106 | goto drop; | 104 | return; |
107 | 105 | ||
108 | rcu_read_lock(); | 106 | rcu_read_lock(); |
109 | proto = rcu_dereference(gre_proto[ver]); | 107 | proto = rcu_dereference(gre_proto[ver]); |
110 | if (!proto || !proto->err_handler) | 108 | if (proto && proto->err_handler) |
111 | goto drop_unlock; | 109 | proto->err_handler(skb, info); |
112 | proto->err_handler(skb, info); | ||
113 | rcu_read_unlock(); | ||
114 | return; | ||
115 | |||
116 | drop_unlock: | ||
117 | rcu_read_unlock(); | 110 | rcu_read_unlock(); |
118 | drop: | ||
119 | kfree_skb(skb); | ||
120 | } | 111 | } |
121 | 112 | ||
122 | static const struct net_protocol net_gre_protocol = { | 113 | static const struct net_protocol net_gre_protocol = { |