From d78f381862c05f299bade9d0a8e4fa822ddeadd1 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 24 Mar 2024 21:29:05 -0400 Subject: [PATCH 1/5] Guard against arithmetic overflows and handle malloc(0) correctly --- tinyalloc.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tinyalloc.c b/tinyalloc.c index eb9fa3f..2108fab 100644 --- a/tinyalloc.c +++ b/tinyalloc.c @@ -159,9 +159,16 @@ static Block *alloc_block(size_t num) { Block *ptr = heap->free; Block *prev = NULL; size_t top = heap->top; - num = (num + heap_alignment - 1) & -heap_alignment; + if (num > -heap_alignment) { + return NULL; // prevent overflow + } + num = (num + heap_alignment - 1) & -heap_alignment; + if (num == 0) { + num = heap_alignment; // prevent zero-size block + } while (ptr != NULL) { - const int is_top = ((size_t)ptr->addr + ptr->size >= top) && ((size_t)ptr->addr + num <= (size_t)heap_limit); + const int is_top = ((size_t)ptr->addr + ptr->size >= top) && + (num <= (size_t)heap_limit - (size_t)ptr->addr); if (is_top || ptr->size >= num) { if (prev != NULL) { prev->next = ptr->next; @@ -199,15 +206,14 @@ static Block *alloc_block(size_t num) { } // no matching free blocks // see if any other blocks available - size_t new_top = top + num; - if (heap->fresh != NULL && new_top <= (size_t)heap_limit) { + if (heap->fresh != NULL && (num <= (size_t)heap_limit - top)) { ptr = heap->fresh; heap->fresh = ptr->next; ptr->addr = (void *)top; ptr->next = heap->used; ptr->size = num; heap->used = ptr; - heap->top = new_top; + heap->top = top + num; return ptr; } return NULL; @@ -235,7 +241,11 @@ static void memclear(void *ptr, size_t num) { } void *ta_calloc(size_t num, size_t size) { + size_t orig = num; num *= size; + if (size != 0 && num / size != orig) { + return NULL; // overflow + } Block *block = alloc_block(num); if (block != NULL) { memclear(block->addr, num); From 1728acae2aa9571c289c721b8027d1d2783ed5b9 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 24 Mar 2024 22:24:07 -0400 Subject: [PATCH 2/5] Add early NULL check to ta_free() (optimization) --- tinyalloc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tinyalloc.c b/tinyalloc.c index 2108fab..148751e 100644 --- a/tinyalloc.c +++ b/tinyalloc.c @@ -134,6 +134,9 @@ bool ta_init(const void *base, const void *limit, const size_t heap_blocks, cons } bool ta_free(void *free) { + if (free == NULL) { + return false; + } Block *block = heap->used; Block *prev = NULL; while (block != NULL) { From 44c4678fa510283882211e90a6bb751e947b052c Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sun, 24 Mar 2024 23:41:44 -0400 Subject: [PATCH 3/5] Implement ta_getsize() and ta_realloc() --- tinyalloc.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- tinyalloc.h | 2 ++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/tinyalloc.c b/tinyalloc.c index 148751e..9fecf50 100644 --- a/tinyalloc.c +++ b/tinyalloc.c @@ -243,6 +243,21 @@ static void memclear(void *ptr, size_t num) { } } +static void memcopy(void *dst, void *src, size_t num) { + size_t *dstw = (size_t *)dst; + size_t *srcw = (size_t *)src; + size_t numw = (num & -sizeof(size_t)) / sizeof(size_t); + while (numw--) { + *dstw++ = *srcw++; + } + num &= (sizeof(size_t) - 1); + uint8_t *dstb = (uint8_t *)dstw; + uint8_t *srcb = (uint8_t *)srcw; + while (num--) { + *dstb++ = *srcb++; + } +} + void *ta_calloc(size_t num, size_t size) { size_t orig = num; num *= size; @@ -251,7 +266,44 @@ void *ta_calloc(size_t num, size_t size) { } Block *block = alloc_block(num); if (block != NULL) { - memclear(block->addr, num); + memclear(block->addr, block->size); + return block->addr; + } + return NULL; +} + +size_t ta_getsize(void *ptr) { + if (ptr == NULL) { + return 0; + } + Block *block = heap->used; + while (block != NULL) { + if (ptr == block->addr) { + return block->size; + } + block = block->next; + } + return 0; +} + +void *ta_realloc(void *ptr, size_t num) { + if (ptr == NULL) { + return ta_alloc(num); + } else if (num == 0) { + ta_free(ptr); + return NULL; + } + size_t size = ta_getsize(ptr); + if (num <= size && size - num <= heap_split_thresh) { + return ptr; // keep current block + } + Block *block = alloc_block(num); + if (block != NULL) { + if (size > num) { + size = num; + } + memcopy(block->addr, ptr, size); + ta_free(ptr); return block->addr; } return NULL; diff --git a/tinyalloc.h b/tinyalloc.h index 113f147..2ea0a2f 100644 --- a/tinyalloc.h +++ b/tinyalloc.h @@ -8,6 +8,8 @@ extern "C" { bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment); void *ta_alloc(size_t num); void *ta_calloc(size_t num, size_t size); +size_t ta_getsize(void *ptr); +void *ta_realloc(void *ptr, size_t num); bool ta_free(void *ptr); size_t ta_num_free(); From fce0c31c86f32fbbd6be32970931af1047488484 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 23 Mar 2024 15:34:13 -0400 Subject: [PATCH 4/5] Optional better C stdlib integration - Optionally set errno to ENOMEM when out of memory - Optionally use memset() to implement memclear() - Optionally use memcpy() to implement memcopy() --- tinyalloc.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/tinyalloc.c b/tinyalloc.c index 9fecf50..96f02d3 100644 --- a/tinyalloc.c +++ b/tinyalloc.c @@ -9,6 +9,12 @@ extern void print_i(size_t); #define print_i(X) #endif +/* optional C stdlib integration */ +#ifdef TA_USE_STDLIB +#include +#include +#endif + typedef struct Block Block; struct Block { @@ -227,9 +233,16 @@ void *ta_alloc(size_t num) { if (block != NULL) { return block->addr; } +#ifdef TA_USE_STDLIB + errno = ENOMEM; +#endif return NULL; } +#ifdef TA_USE_STDLIB +#define memclear(ptr, num) memset((ptr), 0, (num)) +#define memcopy(dst, src, num) memcpy((dst), (src), (num)) +#else static void memclear(void *ptr, size_t num) { size_t *ptrw = (size_t *)ptr; size_t numw = (num & -sizeof(size_t)) / sizeof(size_t); @@ -257,18 +270,22 @@ static void memcopy(void *dst, void *src, size_t num) { *dstb++ = *srcb++; } } +#endif void *ta_calloc(size_t num, size_t size) { size_t orig = num; num *= size; - if (size != 0 && num / size != orig) { - return NULL; // overflow - } - Block *block = alloc_block(num); - if (block != NULL) { - memclear(block->addr, block->size); - return block->addr; + // check for overflow + if (size == 0 || num / size == orig) { + Block *block = alloc_block(num); + if (block != NULL) { + memclear(block->addr, block->size); + return block->addr; + } } +#ifdef TA_USE_STDLIB + errno = ENOMEM; +#endif return NULL; } @@ -306,6 +323,9 @@ void *ta_realloc(void *ptr, size_t num) { ta_free(ptr); return block->addr; } +#ifdef TA_USE_STDLIB + errno = ENOMEM; +#endif return NULL; } From 40e2666697476b3e5d178b334a07c17b5883e179 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Sat, 23 Mar 2024 16:23:10 -0400 Subject: [PATCH 5/5] Allow multiple tinyalloc heaps (eliminate static variables) Multiple heaps can be useful for dividing memory into multiple areas that can be used for different purposes, with individual limits. - Add a config struct (ta_cfg_t, can be declared const) - Pass config pointer to all tinyalloc functions --- tinyalloc.c | 100 +++++++++++++++++++++++++--------------------------- tinyalloc.h | 28 +++++++++------ 2 files changed, 66 insertions(+), 62 deletions(-) diff --git a/tinyalloc.c b/tinyalloc.c index 96f02d3..ffbae9e 100644 --- a/tinyalloc.c +++ b/tinyalloc.c @@ -1,4 +1,5 @@ #include "tinyalloc.h" + #include #ifdef TA_DEBUG @@ -30,19 +31,13 @@ typedef struct { size_t top; // top free addr } Heap; -static Heap *heap = NULL; -static const void *heap_limit = NULL; -static size_t heap_split_thresh; -static size_t heap_alignment; -static size_t heap_max_blocks; - /** * If compaction is enabled, inserts block * into free list, sorted by addr. * If disabled, add block has new head of * the free list. */ -static void insert_block(Block *block) { +static void insert_block(Heap *heap, Block *block) { #ifndef TA_DISABLE_COMPACT Block *ptr = heap->free; Block *prev = NULL; @@ -72,7 +67,7 @@ static void insert_block(Block *block) { } #ifndef TA_DISABLE_COMPACT -static void release_blocks(Block *scan, Block *to) { +static void release_blocks(Heap *heap, Block *scan, Block *to) { Block *scan_next; while (scan != to) { print_s("release"); @@ -86,7 +81,7 @@ static void release_blocks(Block *scan, Block *to) { } } -static void compact() { +static void compact(Heap *heap) { Block *ptr = heap->free; Block *prev; Block *scan; @@ -108,7 +103,7 @@ static void compact() { ptr->size = new_size; Block *next = prev->next; // make merged blocks available - release_blocks(ptr->next, prev->next); + release_blocks(heap, ptr->next, prev->next); // relink ptr->next = next; } @@ -117,32 +112,27 @@ static void compact() { } #endif -bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment) { - heap = (Heap *)base; - heap_limit = limit; - heap_split_thresh = split_thresh; - heap_alignment = alignment; - heap_max_blocks = heap_blocks; - - heap->free = NULL; - heap->used = NULL; - heap->fresh = (Block *)(heap + 1); - heap->top = (size_t)(heap->fresh + heap_blocks); +void ta_init(const ta_cfg_t *cfg) { + Heap *heap = (Heap *)cfg->base; + heap->free = NULL; + heap->used = NULL; + heap->fresh = (Block *)(heap + 1); + heap->top = (size_t)(heap->fresh + cfg->max_blocks); Block *block = heap->fresh; - size_t i = heap_max_blocks - 1; + size_t i = cfg->max_blocks - 1; while (i--) { block->next = block + 1; block++; } block->next = NULL; - return true; } -bool ta_free(void *free) { +bool ta_free(const ta_cfg_t *cfg, void *free) { if (free == NULL) { return false; } + Heap *heap = (Heap *)cfg->base; Block *block = heap->used; Block *prev = NULL; while (block != NULL) { @@ -152,9 +142,9 @@ bool ta_free(void *free) { } else { heap->used = block->next; } - insert_block(block); + insert_block(heap, block); #ifndef TA_DISABLE_COMPACT - compact(); + compact(heap); #endif return true; } @@ -164,20 +154,21 @@ bool ta_free(void *free) { return false; } -static Block *alloc_block(size_t num) { +static Block *alloc_block(const ta_cfg_t *cfg, size_t num) { + Heap *heap = (Heap *)cfg->base; Block *ptr = heap->free; Block *prev = NULL; size_t top = heap->top; - if (num > -heap_alignment) { + if (num > -cfg->alignment) { return NULL; // prevent overflow } - num = (num + heap_alignment - 1) & -heap_alignment; + num = (num + cfg->alignment - 1) & -cfg->alignment; if (num == 0) { - num = heap_alignment; // prevent zero-size block + num = cfg->alignment; // prevent zero-size block } while (ptr != NULL) { const int is_top = ((size_t)ptr->addr + ptr->size >= top) && - (num <= (size_t)heap_limit - (size_t)ptr->addr); + (num <= (size_t)cfg->limit - (size_t)ptr->addr); if (is_top || ptr->size >= num) { if (prev != NULL) { prev->next = ptr->next; @@ -193,7 +184,7 @@ static Block *alloc_block(size_t num) { #ifndef TA_DISABLE_SPLIT } else if (heap->fresh != NULL) { size_t excess = ptr->size - num; - if (excess >= heap_split_thresh) { + if (excess >= cfg->split_thresh) { ptr->size = num; Block *split = heap->fresh; heap->fresh = split->next; @@ -201,9 +192,9 @@ static Block *alloc_block(size_t num) { print_s("split"); print_i((size_t)split->addr); split->size = excess; - insert_block(split); + insert_block(heap, split); #ifndef TA_DISABLE_COMPACT - compact(); + compact(heap); #endif } #endif @@ -215,7 +206,7 @@ static Block *alloc_block(size_t num) { } // no matching free blocks // see if any other blocks available - if (heap->fresh != NULL && (num <= (size_t)heap_limit - top)) { + if (heap->fresh != NULL && (num <= (size_t)cfg->limit - top)) { ptr = heap->fresh; heap->fresh = ptr->next; ptr->addr = (void *)top; @@ -228,8 +219,8 @@ static Block *alloc_block(size_t num) { return NULL; } -void *ta_alloc(size_t num) { - Block *block = alloc_block(num); +void *ta_alloc(const ta_cfg_t *cfg, size_t num) { + Block *block = alloc_block(cfg, num); if (block != NULL) { return block->addr; } @@ -272,12 +263,12 @@ static void memcopy(void *dst, void *src, size_t num) { } #endif -void *ta_calloc(size_t num, size_t size) { +void *ta_calloc(const ta_cfg_t *cfg, size_t num, size_t size) { size_t orig = num; num *= size; // check for overflow if (size == 0 || num / size == orig) { - Block *block = alloc_block(num); + Block *block = alloc_block(cfg, num); if (block != NULL) { memclear(block->addr, block->size); return block->addr; @@ -289,10 +280,11 @@ void *ta_calloc(size_t num, size_t size) { return NULL; } -size_t ta_getsize(void *ptr) { +size_t ta_getsize(const ta_cfg_t *cfg, void *ptr) { if (ptr == NULL) { return 0; } + Heap *heap = (Heap *)cfg->base; Block *block = heap->used; while (block != NULL) { if (ptr == block->addr) { @@ -303,24 +295,24 @@ size_t ta_getsize(void *ptr) { return 0; } -void *ta_realloc(void *ptr, size_t num) { +void *ta_realloc(const ta_cfg_t *cfg, void *ptr, size_t num) { if (ptr == NULL) { - return ta_alloc(num); + return ta_alloc(cfg, num); } else if (num == 0) { - ta_free(ptr); + ta_free(cfg, ptr); return NULL; } - size_t size = ta_getsize(ptr); - if (num <= size && size - num <= heap_split_thresh) { + size_t size = ta_getsize(cfg, ptr); + if (num <= size && size - num <= cfg->split_thresh) { return ptr; // keep current block } - Block *block = alloc_block(num); + Block *block = alloc_block(cfg, num); if (block != NULL) { if (size > num) { size = num; } memcopy(block->addr, ptr, size); - ta_free(ptr); + ta_free(cfg, ptr); return block->addr; } #ifdef TA_USE_STDLIB @@ -338,18 +330,22 @@ static size_t count_blocks(Block *ptr) { return num; } -size_t ta_num_free() { +size_t ta_num_free(const ta_cfg_t *cfg) { + Heap *heap = (Heap *)cfg->base; return count_blocks(heap->free); } -size_t ta_num_used() { +size_t ta_num_used(const ta_cfg_t *cfg) { + Heap *heap = (Heap *)cfg->base; return count_blocks(heap->used); } -size_t ta_num_fresh() { +size_t ta_num_fresh(const ta_cfg_t *cfg) { + Heap *heap = (Heap *)cfg->base; return count_blocks(heap->fresh); } -bool ta_check() { - return heap_max_blocks == ta_num_free() + ta_num_used() + ta_num_fresh(); +bool ta_check(const ta_cfg_t *cfg) { + return cfg->max_blocks == + ta_num_free(cfg) + ta_num_used(cfg) + ta_num_fresh(cfg); } diff --git a/tinyalloc.h b/tinyalloc.h index 2ea0a2f..55b44ca 100644 --- a/tinyalloc.h +++ b/tinyalloc.h @@ -5,17 +5,25 @@ extern "C" { #include #include -bool ta_init(const void *base, const void *limit, const size_t heap_blocks, const size_t split_thresh, const size_t alignment); -void *ta_alloc(size_t num); -void *ta_calloc(size_t num, size_t size); -size_t ta_getsize(void *ptr); -void *ta_realloc(void *ptr, size_t num); -bool ta_free(void *ptr); +typedef struct { + void *base; + void *limit; + size_t max_blocks; + size_t split_thresh; + size_t alignment; +} ta_cfg_t; -size_t ta_num_free(); -size_t ta_num_used(); -size_t ta_num_fresh(); -bool ta_check(); +void ta_init(const ta_cfg_t *cfg); +void *ta_alloc(const ta_cfg_t *cfg, size_t num); +void *ta_calloc(const ta_cfg_t *cfg, size_t num, size_t size); +size_t ta_getsize(const ta_cfg_t *cfg, void *ptr); +void *ta_realloc(const ta_cfg_t *cfg, void *ptr, size_t num); +bool ta_free(const ta_cfg_t *cfg, void *ptr); + +size_t ta_num_free(const ta_cfg_t *cfg); +size_t ta_num_used(const ta_cfg_t *cfg); +size_t ta_num_fresh(const ta_cfg_t *cfg); +bool ta_check(const ta_cfg_t *cfg); #ifdef __cplusplus }