Skip to content

Commit a2152fe

Browse files
yadan-fanakpm00
authored andcommitted
mm: mempool: fix crash in mempool_free() for zero-minimum pools
The mempool wake-up fix introduced in commit a5867a2 ("mm: mempool: fix wake-up edge case bug for zero-minimum pools") inlined the add_element() logic in mempool_free() to return the element to the zero-minimum pool: pool->elements[pool->curr_nr++] = element; This causes crash, because mempool_init_node() does not initialize with real allocation for zero-minimum pool, it only returns ZERO_SIZE_PTR to the elements array which is unable to be dereferenced, and the pre-allocation of this array never happened since the while test: while (pool->curr_nr < pool->min_nr) can never be satisfied as min_nr is zero, so the pool does not actually reserve any buffer, the only way so far is to call alloc_fn() to get buffer from SLUB, but if the memory is under high pressure the alloc_fn() could never get any buffer, the waiting thread would be in an indefinite loop of wake-sleep in a period until there is free memory to get. This patch changes mempool_init_node() to allocate 1 element for the elements array of zero-minimum pool, so that the pool will have reserved buffer to use. This will fix the crash issue and let the waiting thread can get the reserved element when alloc_fn() failed to get buffer under high memory pressure. Also modify add_element() to support zero-minimum pool with simplifying codes of zero-minimum handling in mempool_free(). Link: https://lkml.kernel.org/r/[email protected] Fixes: a5867a2 ("mm: mempool: fix wake-up edge case bug for zero-minimum pools") Signed-off-by: Yadan Fan <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent f04fd85 commit a2152fe

File tree

1 file changed

+10
-14
lines changed

1 file changed

+10
-14
lines changed

mm/mempool.c

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ static void kasan_unpoison_element(mempool_t *pool, void *element)
136136

137137
static __always_inline void add_element(mempool_t *pool, void *element)
138138
{
139-
BUG_ON(pool->curr_nr >= pool->min_nr);
139+
BUG_ON(pool->min_nr != 0 && pool->curr_nr >= pool->min_nr);
140140
poison_element(pool, element);
141141
if (kasan_poison_element(pool, element))
142142
pool->elements[pool->curr_nr++] = element;
@@ -202,16 +202,20 @@ int mempool_init_node(mempool_t *pool, int min_nr, mempool_alloc_t *alloc_fn,
202202
pool->alloc = alloc_fn;
203203
pool->free = free_fn;
204204
init_waitqueue_head(&pool->wait);
205-
206-
pool->elements = kmalloc_array_node(min_nr, sizeof(void *),
205+
/*
206+
* max() used here to ensure storage for at least 1 element to support
207+
* zero minimum pool
208+
*/
209+
pool->elements = kmalloc_array_node(max(1, min_nr), sizeof(void *),
207210
gfp_mask, node_id);
208211
if (!pool->elements)
209212
return -ENOMEM;
210213

211214
/*
212-
* First pre-allocate the guaranteed number of buffers.
215+
* First pre-allocate the guaranteed number of buffers,
216+
* also pre-allocate 1 element for zero minimum pool.
213217
*/
214-
while (pool->curr_nr < pool->min_nr) {
218+
while (pool->curr_nr < max(1, pool->min_nr)) {
215219
void *element;
216220

217221
element = pool->alloc(gfp_mask, pool->pool_data);
@@ -555,20 +559,12 @@ void mempool_free(void *element, mempool_t *pool)
555559
* wake-up path of previous test. This explicit check ensures the
556560
* allocation of element when both min_nr and curr_nr are 0, and
557561
* any active waiters are properly awakened.
558-
*
559-
* Inline the same logic as previous test, add_element() cannot be
560-
* directly used here since it has BUG_ON to deny if min_nr equals
561-
* curr_nr, so here picked rest of add_element() to use without
562-
* BUG_ON check.
563562
*/
564563
if (unlikely(pool->min_nr == 0 &&
565564
READ_ONCE(pool->curr_nr) == 0)) {
566565
spin_lock_irqsave(&pool->lock, flags);
567566
if (likely(pool->curr_nr == 0)) {
568-
/* Inline the logic of add_element() */
569-
poison_element(pool, element);
570-
if (kasan_poison_element(pool, element))
571-
pool->elements[pool->curr_nr++] = element;
567+
add_element(pool, element);
572568
spin_unlock_irqrestore(&pool->lock, flags);
573569
if (wq_has_sleeper(&pool->wait))
574570
wake_up(&pool->wait);

0 commit comments

Comments
 (0)