diff options
author | Alexey Kuznetsov <kuznet@ms2.inr.ac.ru> | 2006-07-25 16:45:12 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2006-07-25 16:45:12 -0700 |
commit | 722874909271a807b243a797c2958e0a12992964 (patch) | |
tree | 341d9c20eacdf8f452d8e2eea61fe2d40923179d | |
parent | b6e77a5346d8a739227ed73c2269966a4fd652b4 (diff) |
[IPV4] ipmr: ip multicast route bug fix.
IP multicast route code was reusing an skb which causes use after free
and double free.
From: Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
Note, it is real skb_clone(), not alloc_skb(). Equeued skb contains
the whole half-prepared netlink message plus room for the rest.
It could be also skb_copy(), if we want to be puristic about mangling
cloned data, but original copy is really not going to be used.
Acked-by: Stephen Hemminger <shemminger@osdl.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/ipmr.c | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 9ccacf57f08..85893eef6b1 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1578,6 +1578,7 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait) cache = ipmr_cache_find(rt->rt_src, rt->rt_dst); if (cache==NULL) { + struct sk_buff *skb2; struct net_device *dev; int vif; @@ -1591,12 +1592,18 @@ int ipmr_get_route(struct sk_buff *skb, struct rtmsg *rtm, int nowait) read_unlock(&mrt_lock); return -ENODEV; } - skb->nh.raw = skb_push(skb, sizeof(struct iphdr)); - skb->nh.iph->ihl = sizeof(struct iphdr)>>2; - skb->nh.iph->saddr = rt->rt_src; - skb->nh.iph->daddr = rt->rt_dst; - skb->nh.iph->version = 0; - err = ipmr_cache_unresolved(vif, skb); + skb2 = skb_clone(skb, GFP_ATOMIC); + if (!skb2) { + read_unlock(&mrt_lock); + return -ENOMEM; + } + + skb2->nh.raw = skb_push(skb2, sizeof(struct iphdr)); + skb2->nh.iph->ihl = sizeof(struct iphdr)>>2; + skb2->nh.iph->saddr = rt->rt_src; + skb2->nh.iph->daddr = rt->rt_dst; + skb2->nh.iph->version = 0; + err = ipmr_cache_unresolved(vif, skb2); read_unlock(&mrt_lock); return err; } |