diff options
author | David S. Miller <davem@davemloft.net> | 2015-03-16 05:22:17 +0300 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-03-16 05:22:17 +0300 |
commit | 7993d44ea1f7b17dd17863ab139d2c9df17dfe51 (patch) | |
tree | 6be3f18d0d0311eb84062e18d30b771b14bb9f9e | |
parent | 0034de4193e4aad30bbbef4e74ca5e0631ba08a7 (diff) | |
parent | 565e86404e4c40e03f602ef0d6d490328f28c493 (diff) | |
download | linux-7993d44ea1f7b17dd17863ab139d2c9df17dfe51.tar.xz |
Merge branch 'rhashtable-fixes-next'
Herbert Xu says:
====================
rhashtable: Fix two bugs caused by multiple rehash preparation
While testing some new patches over the weekend I discovered a
couple of bugs in the series that had just been merged. These
two patches fix them:
1) A use-after-free in the walker that can cause crashes when
walking during a rehash.
2) When a second rehash starts during a single rhashtable_remove
call the remove may fail when it shouldn't.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | lib/rhashtable.c | 24 |
1 files changed, 11 insertions, 13 deletions
diff --git a/lib/rhashtable.c b/lib/rhashtable.c index 9d53a46dcca9..c523d3a563aa 100644 --- a/lib/rhashtable.c +++ b/lib/rhashtable.c @@ -511,28 +511,25 @@ static bool __rhashtable_remove(struct rhashtable *ht, */ bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) { - struct bucket_table *tbl, *old_tbl; + struct bucket_table *tbl; bool ret; rcu_read_lock(); - old_tbl = rht_dereference_rcu(ht->tbl, ht); - ret = __rhashtable_remove(ht, old_tbl, obj); + tbl = rht_dereference_rcu(ht->tbl, ht); /* Because we have already taken (and released) the bucket * lock in old_tbl, if we find that future_tbl is not yet * visible then that guarantees the entry to still be in - * old_tbl if it exists. + * the old tbl if it exists. */ - tbl = rht_dereference_rcu(old_tbl->future_tbl, ht) ?: old_tbl; - if (!ret && old_tbl != tbl) - ret = __rhashtable_remove(ht, tbl, obj); + while (!(ret = __rhashtable_remove(ht, tbl, obj)) && + (tbl = rht_dereference_rcu(tbl->future_tbl, ht))) + ; if (ret) { - bool no_resize_running = tbl == old_tbl; - atomic_dec(&ht->nelems); - if (no_resize_running && rht_shrink_below_30(ht, tbl)) + if (rht_shrink_below_30(ht, tbl)) schedule_work(&ht->run_work); } @@ -854,10 +851,8 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) struct rhashtable *ht; struct bucket_table *tbl = iter->walker->tbl; - rcu_read_unlock(); - if (!tbl) - return; + goto out; ht = iter->ht; @@ -869,6 +864,9 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) mutex_unlock(&ht->mutex); iter->p = NULL; + +out: + rcu_read_unlock(); } EXPORT_SYMBOL_GPL(rhashtable_walk_stop); |