malloc: check for underflow in blk_slice

This commit is contained in:
anna 2021-08-09 23:06:50 +02:00
parent 1d30394cec
commit faa99622df
Signed by: fef
GPG key ID: EC22E476DC2D3D84

View file

@ -133,6 +133,8 @@ size_t atomic_heap_overhead = OVERHEAD;
static size_t blk_get_size(struct memblk *blk); static size_t blk_get_size(struct memblk *blk);
/** @brief Set the usable block size without overhead and without affecting flags. */ /** @brief Set the usable block size without overhead and without affecting flags. */
static void blk_set_size(struct memblk *blk, size_t size); 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. */ /** @brief Flag a block as allocated. */
static void blk_set_alloc(struct memblk *blk); static void blk_set_alloc(struct memblk *blk);
/** @brief Remove the allocated flag from a block. */ /** @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 * to replace the division/multiplication pair with a bitfield clear
* instruction (MIN_SIZE is always a power of two), so this is okay. * instruction (MIN_SIZE is always a power of two), so this is okay.
*/ */
size_t original_size = size; size = round_alloc_size_up(size);
size = (size / MIN_SIZE) * MIN_SIZE;
if (size < original_size)
size += MIN_SIZE;
mutex_lock(&generic_heap_lock); mutex_lock(&generic_heap_lock);
@ -236,10 +235,7 @@ void *atomic_malloc(size_t size)
if (size > atomic_heap_free) if (size > atomic_heap_free)
return NULL; return NULL;
size_t original_size = size; size = round_alloc_size_up(size);
size = (size / MIN_SIZE) * MIN_SIZE;
if (size < original_size)
size += MIN_SIZE;
atomic_enter(); atomic_enter();
@ -369,10 +365,16 @@ static struct memblk *blk_slice(struct list_head *heap, struct memblk *blk, size
{ {
list_delete(&blk->list); 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; 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); blk_set_alloc(blk);
return blk; /* hand out the entire block */ return blk;
} }
if (heap == &atomic_heap) { if (heap == &atomic_heap) {
@ -403,6 +405,14 @@ static struct memblk *blk_slice(struct list_head *heap, struct memblk *blk, size
return blk; 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) static inline size_t blk_get_size(struct memblk *blk)
{ {
return blk->size & SIZE_MSK; return blk->size & SIZE_MSK;