aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPavel Emelyanov <xemul@openvz.org>2008-02-23 22:57:02 -0500
committerDavid S. Miller <davem@davemloft.net>2008-02-23 22:57:02 -0500
commitbc4bf5f38cf0a623e6a29f52ec80bfcc56a373c6 (patch)
tree44c9dab22f5d75d797845b54df07bd6da40bcc7e /net
parent1840bb13c22f5b8fd2e242e36c8d6ea3f312be67 (diff)
[NEIGH]: Fix race between neighbor lookup and table's hash_rnd update.
The neigh_hash_grow() may update the tbl->hash_rnd value, which is used in all tbl->hash callbacks to calculate the hashval. Two lookup routines may race with this, since they call the ->hash callback without the tbl->lock held. Since the hash_rnd is changed with this lock write-locked moving the calls to ->hash under this lock read-locked closes this gap. Signed-off-by: Pavel Emelyanov <xemul@openvz.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/neighbour.c6
1 files changed, 4 insertions, 2 deletions
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 4062b88bfc1b..2328acbd16cd 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -358,11 +358,12 @@ struct neighbour *neigh_lookup(struct neigh_table *tbl, const void *pkey,
358{ 358{
359 struct neighbour *n; 359 struct neighbour *n;
360 int key_len = tbl->key_len; 360 int key_len = tbl->key_len;
361 u32 hash_val = tbl->hash(pkey, dev); 361 u32 hash_val;
362 362
363 NEIGH_CACHE_STAT_INC(tbl, lookups); 363 NEIGH_CACHE_STAT_INC(tbl, lookups);
364 364
365 read_lock_bh(&tbl->lock); 365 read_lock_bh(&tbl->lock);
366 hash_val = tbl->hash(pkey, dev);
366 for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) { 367 for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {
367 if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) { 368 if (dev == n->dev && !memcmp(n->primary_key, pkey, key_len)) {
368 neigh_hold(n); 369 neigh_hold(n);
@@ -379,11 +380,12 @@ struct neighbour *neigh_lookup_nodev(struct neigh_table *tbl, struct net *net,
379{ 380{
380 struct neighbour *n; 381 struct neighbour *n;
381 int key_len = tbl->key_len; 382 int key_len = tbl->key_len;
382 u32 hash_val = tbl->hash(pkey, NULL); 383 u32 hash_val;
383 384
384 NEIGH_CACHE_STAT_INC(tbl, lookups); 385 NEIGH_CACHE_STAT_INC(tbl, lookups);
385 386
386 read_lock_bh(&tbl->lock); 387 read_lock_bh(&tbl->lock);
388 hash_val = tbl->hash(pkey, NULL);
387 for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) { 389 for (n = tbl->hash_buckets[hash_val & tbl->hash_mask]; n; n = n->next) {
388 if (!memcmp(n->primary_key, pkey, key_len) && 390 if (!memcmp(n->primary_key, pkey, key_len) &&
389 (net == n->dev->nd_net)) { 391 (net == n->dev->nd_net)) {