Commit ba7c95ea authored by Herbert Xu's avatar Herbert Xu Committed by David S. Miller

rhashtable: Fix sleeping inside RCU critical section in walk_stop

The commit 963ecbd4 ("rhashtable:
Fix use-after-free in rhashtable_walk_stop") fixed a real bug
but created another one because we may end up sleeping inside an
RCU critical section.

This patch fixes it properly by replacing the mutex with a spin
lock that specifically protects the walker lists.
Reported-by: default avatarSasha Levin <sasha.levin@oracle.com>
Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ce046c56
...@@ -133,6 +133,7 @@ struct rhashtable_params { ...@@ -133,6 +133,7 @@ struct rhashtable_params {
* @p: Configuration parameters * @p: Configuration parameters
* @run_work: Deferred worker to expand/shrink asynchronously * @run_work: Deferred worker to expand/shrink asynchronously
* @mutex: Mutex to protect current/future table swapping * @mutex: Mutex to protect current/future table swapping
* @lock: Spin lock to protect walker list
* @being_destroyed: True if table is set up for destruction * @being_destroyed: True if table is set up for destruction
*/ */
struct rhashtable { struct rhashtable {
...@@ -144,6 +145,7 @@ struct rhashtable { ...@@ -144,6 +145,7 @@ struct rhashtable {
struct rhashtable_params p; struct rhashtable_params p;
struct work_struct run_work; struct work_struct run_work;
struct mutex mutex; struct mutex mutex;
spinlock_t lock;
}; };
/** /**
......
...@@ -256,8 +256,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht) ...@@ -256,8 +256,10 @@ static int rhashtable_rehash_table(struct rhashtable *ht)
/* Publish the new table pointer. */ /* Publish the new table pointer. */
rcu_assign_pointer(ht->tbl, new_tbl); rcu_assign_pointer(ht->tbl, new_tbl);
spin_lock(&ht->lock);
list_for_each_entry(walker, &old_tbl->walkers, list) list_for_each_entry(walker, &old_tbl->walkers, list)
walker->tbl = NULL; walker->tbl = NULL;
spin_unlock(&ht->lock);
/* Wait for readers. All new readers will see the new /* Wait for readers. All new readers will see the new
* table, and thus no references to the old table will * table, and thus no references to the old table will
...@@ -635,12 +637,12 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter) ...@@ -635,12 +637,12 @@ void rhashtable_walk_stop(struct rhashtable_iter *iter)
ht = iter->ht; ht = iter->ht;
mutex_lock(&ht->mutex); spin_lock(&ht->lock);
if (tbl->rehash < tbl->size) if (tbl->rehash < tbl->size)
list_add(&iter->walker->list, &tbl->walkers); list_add(&iter->walker->list, &tbl->walkers);
else else
iter->walker->tbl = NULL; iter->walker->tbl = NULL;
mutex_unlock(&ht->mutex); spin_unlock(&ht->lock);
iter->p = NULL; iter->p = NULL;
...@@ -723,6 +725,7 @@ int rhashtable_init(struct rhashtable *ht, ...@@ -723,6 +725,7 @@ int rhashtable_init(struct rhashtable *ht,
memset(ht, 0, sizeof(*ht)); memset(ht, 0, sizeof(*ht));
mutex_init(&ht->mutex); mutex_init(&ht->mutex);
spin_lock_init(&ht->lock);
memcpy(&ht->p, params, sizeof(*params)); memcpy(&ht->p, params, sizeof(*params));
if (params->min_size) if (params->min_size)
......
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