forked from mirrors/linux
		
	rhashtable: Fix sleeping inside RCU critical section in walk_stop
The commit 963ecbd41a ("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: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									ce046c568c
								
							
						
					
					
						commit
						ba7c95ea38
					
				
					 2 changed files with 7 additions and 2 deletions
				
			
		| 
						 | 
					@ -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 {
 | 
				
			||||||
	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)
 | 
				
			||||||
	/* 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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue