Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+11 −13
Original line number Diff line number Diff line
@@ -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);