diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-08 20:56:14 +0000 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-08-09 21:47:14 -0700 |
commit | 9de79c127cccecb11ae6a21ab1499e87aa222880 (patch) | |
tree | 98173bcb0f620d078edc85f577b778b1f840ec3e /net/core/dst.c | |
parent | 19fd61785a580c60cba900c5171bfadb57dd5056 (diff) | |
download | linux-stable-9de79c127cccecb11ae6a21ab1499e87aa222880.tar.gz linux-stable-9de79c127cccecb11ae6a21ab1499e87aa222880.tar.bz2 linux-stable-9de79c127cccecb11ae6a21ab1499e87aa222880.zip |
net: fix potential neighbour race in dst_ifdown()
Followup of commit f2c31e32b378a (fix NULL dereferences in
check_peer_redir()).
We need to make sure dst neighbour doesnt change in dst_ifdown().
Fix some sparse errors.
Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/dst.c')
-rw-r--r-- | net/core/dst.c | 15 |
1 files changed, 10 insertions, 5 deletions
diff --git a/net/core/dst.c b/net/core/dst.c index 14b33baf0733..d5e2c4c09107 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -171,7 +171,7 @@ void *dst_alloc(struct dst_ops *ops, struct net_device *dev, dst_init_metrics(dst, dst_default_metrics, true); dst->expires = 0UL; dst->path = dst; - dst->_neighbour = NULL; + RCU_INIT_POINTER(dst->_neighbour, NULL); #ifdef CONFIG_XFRM dst->xfrm = NULL; #endif @@ -229,11 +229,11 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) smp_rmb(); again: - neigh = dst->_neighbour; + neigh = rcu_dereference_protected(dst->_neighbour, 1); child = dst->child; if (neigh) { - dst->_neighbour = NULL; + RCU_INIT_POINTER(dst->_neighbour, NULL); neigh_release(neigh); } @@ -360,14 +360,19 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, if (!unregister) { dst->input = dst->output = dst_discard; } else { + struct neighbour *neigh; + dst->dev = dev_net(dst->dev)->loopback_dev; dev_hold(dst->dev); dev_put(dev); - if (dst->_neighbour && dst->_neighbour->dev == dev) { - dst->_neighbour->dev = dst->dev; + rcu_read_lock(); + neigh = dst_get_neighbour(dst); + if (neigh && neigh->dev == dev) { + neigh->dev = dst->dev; dev_hold(dst->dev); dev_put(dev); } + rcu_read_unlock(); } } |