diff --git a/lib/malloc.c b/lib/malloc.c index 0eca064..188456a 100644 --- a/lib/malloc.c +++ b/lib/malloc.c @@ -133,6 +133,8 @@ size_t atomic_heap_overhead = OVERHEAD; static size_t blk_get_size(struct memblk *blk); /** @brief Set the usable block size without overhead and without affecting flags. */ static void blk_set_size(struct memblk *blk, size_t size); +/** @brief Round up towards the next multiple of `MIN_SIZE`. */ +static size_t round_alloc_size_up(size_t size); /** @brief Flag a block as allocated. */ static void blk_set_alloc(struct memblk *blk); /** @brief Remove the allocated flag from a block. */ @@ -202,10 +204,7 @@ void *malloc(size_t size) * to replace the division/multiplication pair with a bitfield clear * instruction (MIN_SIZE is always a power of two), so this is okay. */ - size_t original_size = size; - size = (size / MIN_SIZE) * MIN_SIZE; - if (size < original_size) - size += MIN_SIZE; + size = round_alloc_size_up(size); mutex_lock(&generic_heap_lock); @@ -236,10 +235,7 @@ void *atomic_malloc(size_t size) if (size > atomic_heap_free) return NULL; - size_t original_size = size; - size = (size / MIN_SIZE) * MIN_SIZE; - if (size < original_size) - size += MIN_SIZE; + size = round_alloc_size_up(size); atomic_enter(); @@ -369,10 +365,16 @@ static struct memblk *blk_slice(struct list_head *heap, struct memblk *blk, size { list_delete(&blk->list); + /* + * If the remaining size is less than the minimum allocation unit, we + * hand out the entire block. Additionally, we must add an underflow + * check which happens if the slice size is less than OVERHEAD smaller + * than the full block size. + */ size_t rest_size = blk_get_size(blk) - slice_size - OVERHEAD; - if (rest_size < MIN_SIZE) { + if (rest_size < MIN_SIZE || rest_size > blk_get_size(blk)) { blk_set_alloc(blk); - return blk; /* hand out the entire block */ + return blk; } if (heap == &atomic_heap) { @@ -403,6 +405,14 @@ static struct memblk *blk_slice(struct list_head *heap, struct memblk *blk, size return blk; } +static inline size_t round_alloc_size_up(size_t size) +{ + size_t rounded = (size / MIN_SIZE) * MIN_SIZE; + if (rounded < size) + rounded += MIN_SIZE; + return rounded; +} + static inline size_t blk_get_size(struct memblk *blk) { return blk->size & SIZE_MSK;