heap: Fix race condition causing malloc() to fail under some conditions

During a call to multi_heap_malloc(), if both these conditions were true:
- That heap only has one block large enough for the allocation
  (this is always the case if the heap is unfragmented).
- Another allocation is simultaneously occurring in the same heap.

... multi_heap_malloc() could incorrectly return NULL.

This caused IDF heap_caps_malloc() and malloc() to also fail, particularly
often if only one or two heaps had space for the allocation (otherwise
heap_caps_malloc() fails over to the next heap).
This commit is contained in:
Angus Gratton 2017-10-19 15:59:04 +08:00 committed by Angus Gratton
parent 2e8441df9e
commit b0c5665f15

View file

@ -337,12 +337,22 @@ void *multi_heap_malloc_impl(multi_heap_handle_t heap, size_t size)
size_t best_size = SIZE_MAX; size_t best_size = SIZE_MAX;
size = ALIGN_UP(size); size = ALIGN_UP(size);
if (size == 0 || heap == NULL || heap->free_bytes < size) { if (size == 0 || heap == NULL) {
return NULL; return NULL;
} }
MULTI_HEAP_LOCK(heap->lock); MULTI_HEAP_LOCK(heap->lock);
/* Note: this check must be done while holding the lock as both
malloc & realloc may temporarily shrink the free_bytes value
before they split a large block. This can result in false negatives,
especially if the heap is unfragmented.
*/
if (heap->free_bytes < size) {
MULTI_HEAP_UNLOCK(heap->lock);
return NULL;
}
/* Find best free block to perform the allocation in */ /* Find best free block to perform the allocation in */
prev = &heap->first_block; prev = &heap->first_block;
for (heap_block_t *b = heap->first_block.next_free; b != NULL; b = b->next_free) { for (heap_block_t *b = heap->first_block.next_free; b != NULL; b = b->next_free) {