Commit 7993d44e authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 0034de41 565e8640
...@@ -511,28 +511,25 @@ static bool __rhashtable_remove(struct rhashtable *ht, ...@@ -511,28 +511,25 @@ static bool __rhashtable_remove(struct rhashtable *ht,
*/ */
bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj) bool rhashtable_remove(struct rhashtable *ht, struct rhash_head *obj)
{ {
struct bucket_table *tbl, *old_tbl; struct bucket_table *tbl;
bool ret; bool ret;
rcu_read_lock(); rcu_read_lock();
old_tbl = rht_dereference_rcu(ht->tbl, ht); tbl = rht_dereference_rcu(ht->tbl, ht);
ret = __rhashtable_remove(ht, old_tbl, obj);
/* Because we have already taken (and released) the bucket /* Because we have already taken (and released) the bucket
* lock in old_tbl, if we find that future_tbl is not yet * lock in old_tbl, if we find that future_tbl is not yet
* visible then that guarantees the entry to still be in * 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; while (!(ret = __rhashtable_remove(ht, tbl, obj)) &&
if (!ret && old_tbl != tbl) (tbl = rht_dereference_rcu(tbl->future_tbl, ht)))
ret = __rhashtable_remove(ht, tbl, obj); ;
if (ret) { if (ret) {
bool no_resize_running = tbl == old_tbl;
atomic_dec(&ht->nelems); 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); schedule_work(&ht->run_work);
} }
...@@ -854,10 +851,8 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) ...@@ -854,10 +851,8 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter)
struct rhashtable *ht; struct rhashtable *ht;
struct bucket_table *tbl = iter->walker->tbl; struct bucket_table *tbl = iter->walker->tbl;
rcu_read_unlock();
if (!tbl) if (!tbl)
return; goto out;
ht = iter->ht; ht = iter->ht;
...@@ -869,6 +864,9 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) ...@@ -869,6 +864,9 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter)
mutex_unlock(&ht->mutex); mutex_unlock(&ht->mutex);
iter->p = NULL; iter->p = NULL;
out:
rcu_read_unlock();
} }
EXPORT_SYMBOL_GPL(rhashtable_walk_stop); EXPORT_SYMBOL_GPL(rhashtable_walk_stop);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment