diff options
author | Eric Dumazet <eric.dumazet@gmail.com> | 2011-08-08 16:56:14 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-08-10 00:47:14 -0400 |
commit | 9de79c127cccecb11ae6a21ab1499e87aa222880 (patch) | |
tree | 98173bcb0f620d078edc85f577b778b1f840ec3e | |
parent | 19fd61785a580c60cba900c5171bfadb57dd5056 (diff) |
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>
-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, | |||
171 | dst_init_metrics(dst, dst_default_metrics, true); | 171 | dst_init_metrics(dst, dst_default_metrics, true); |
172 | dst->expires = 0UL; | 172 | dst->expires = 0UL; |
173 | dst->path = dst; | 173 | dst->path = dst; |
174 | dst->_neighbour = NULL; | 174 | RCU_INIT_POINTER(dst->_neighbour, NULL); |
175 | #ifdef CONFIG_XFRM | 175 | #ifdef CONFIG_XFRM |
176 | dst->xfrm = NULL; | 176 | dst->xfrm = NULL; |
177 | #endif | 177 | #endif |
@@ -229,11 +229,11 @@ struct dst_entry *dst_destroy(struct dst_entry * dst) | |||
229 | smp_rmb(); | 229 | smp_rmb(); |
230 | 230 | ||
231 | again: | 231 | again: |
232 | neigh = dst->_neighbour; | 232 | neigh = rcu_dereference_protected(dst->_neighbour, 1); |
233 | child = dst->child; | 233 | child = dst->child; |
234 | 234 | ||
235 | if (neigh) { | 235 | if (neigh) { |
236 | dst->_neighbour = NULL; | 236 | RCU_INIT_POINTER(dst->_neighbour, NULL); |
237 | neigh_release(neigh); | 237 | neigh_release(neigh); |
238 | } | 238 | } |
239 | 239 | ||
@@ -360,14 +360,19 @@ static void dst_ifdown(struct dst_entry *dst, struct net_device *dev, | |||
360 | if (!unregister) { | 360 | if (!unregister) { |
361 | dst->input = dst->output = dst_discard; | 361 | dst->input = dst->output = dst_discard; |
362 | } else { | 362 | } else { |
363 | struct neighbour *neigh; | ||
364 | |||
363 | dst->dev = dev_net(dst->dev)->loopback_dev; | 365 | dst->dev = dev_net(dst->dev)->loopback_dev; |
364 | dev_hold(dst->dev); | 366 | dev_hold(dst->dev); |
365 | dev_put(dev); | 367 | dev_put(dev); |
366 | if (dst->_neighbour && dst->_neighbour->dev == dev) { | 368 | rcu_read_lock(); |
367 | dst->_neighbour->dev = dst->dev; | 369 | neigh = dst_get_neighbour(dst); |
370 | if (neigh && neigh->dev == dev) { | ||
371 | neigh->dev = dst->dev; | ||
368 | dev_hold(dst->dev); | 372 | dev_hold(dst->dev); |
369 | dev_put(dev); | 373 | dev_put(dev); |
370 | } | 374 | } |
375 | rcu_read_unlock(); | ||
371 | } | 376 | } |
372 | } | 377 | } |
373 | 378 | ||