tunnels: Fix tunnels change rcu protection
authorPavel Emelyanov <xemul@parallels.com>
Wed, 27 Oct 2010 05:43:53 +0000 (05:43 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 27 Oct 2010 21:20:08 +0000 (14:20 -0700)
After making rcu protection for tunnels (ipip, gre, sit and ip6) a bug
was introduced into the SIOCCHGTUNNEL code.

The tunnel is first unlinked, then addresses change, then it is linked
back probably into another bucket. But while changing the parms, the
hash table is unlocked to readers and they can lookup the improper tunnel.

Respective commits are b7285b79 (ipip: get rid of ipip_lock), 1507850b
(gre: get rid of ipgre_lock), 3a43be3c (sit: get rid of ipip6_lock) and
94767632 (ip6tnl: get rid of ip6_tnl_lock).

The quick fix is to wait for quiescent state to pass after unlinking,
but if it is inappropriate I can invent something better, just let me
know.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv4/ip_gre.c
net/ipv4/ipip.c
net/ipv6/ip6_tunnel.c
net/ipv6/sit.c

index d0ffcbe369b76b4a000c5f5e23b6e61dc64fb3dd..01087e035b7d6632f9543d36ebfbf025212fc8a9 100644 (file)
@@ -1072,6 +1072,7 @@ ipgre_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                        break;
                                }
                                ipgre_tunnel_unlink(ign, t);
+                               synchronize_net();
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                t->parms.i_key = p.i_key;
index e9b816e6cd73a681ea02e9540d6849eb924f4a8a..cd300aaee78f542630f40ab9a34f9b10af8eec9e 100644 (file)
@@ -676,6 +676,7 @@ ipip_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                }
                                t = netdev_priv(dev);
                                ipip_tunnel_unlink(ipn, t);
+                               synchronize_net();
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                memcpy(dev->dev_addr, &p.iph.saddr, 4);
index 38b9a56c173f54f7861a07483446a1e45dcf598a..2a59610c2a582ed885ae756729b58da00e6f324f 100644 (file)
@@ -1284,6 +1284,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
                                t = netdev_priv(dev);
 
                        ip6_tnl_unlink(ip6n, t);
+                       synchronize_net();
                        err = ip6_tnl_change(t, &p);
                        ip6_tnl_link(ip6n, t);
                        netdev_state_change(dev);
index 367a6cc584ccc40bb1c5a7fc03022bd89f304d10..d6bfaec3bbbf1a91701fd31616757af1a565bb41 100644 (file)
@@ -963,6 +963,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
                                }
                                t = netdev_priv(dev);
                                ipip6_tunnel_unlink(sitn, t);
+                               synchronize_net();
                                t->parms.iph.saddr = p.iph.saddr;
                                t->parms.iph.daddr = p.iph.daddr;
                                memcpy(dev->dev_addr, &p.iph.saddr, 4);