diff --git a/freelist/Makefile b/freelist/Makefile index f2e1377..fa96c24 100644 --- a/freelist/Makefile +++ b/freelist/Makefile @@ -1,10 +1,8 @@ CC = gcc CFLAGS = -Wall -O2 -CFLAGS += -DFREELIST_ALIGN - TARGET = main.elf -SRC = main.c freelist.c +SRC = main.c freelist.c hexdump.c #LDFLAGS = diff --git a/freelist/freelist.c b/freelist/freelist.c index f9dc531..7b181ae 100644 --- a/freelist/freelist.c +++ b/freelist/freelist.c @@ -1,37 +1,54 @@ #include "freelist.h" -#include "stddef.h" -#include -#include -#include -#include -#include -struct FreeListBlock { - struct FreeListBlock *next; +struct __attribute__((packed)) FreeListBlock { + uint16_t next_offset; }; -/* Initialize the FreeList */ -int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize) { - size_t size = ALIGN(itemsize); +/* Align to nearest multiple of align */ +static inline size_t align_up_to(size_t n, size_t align) { + return (n + align - 1) & ~(align - 1); +} - if (!fl || end <= start) - return EXIT_FAILURE; +// Convert pointer -> 1-based offset (0 means NULL) +static inline uint32_t ptr_to_offset(FreeList *fl, void *ptr) { + if (!ptr) + return 0; // NULL maps to 0 + uintptr_t diff = (uintptr_t)ptr - fl->start; + return (uint32_t)(diff / fl->size) + 1; +} + +// Convert 1-based offset -> pointer (0 means NULL) +static inline void *offset_to_ptr(FreeList *fl, uint32_t offset) { + if (offset == 0) + return NULL; // 0 = invalid/null + return (void *)(fl->start + (uintptr_t)(offset - 1) * fl->size); +} + +/* Initialize the FreeList */ +int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize) { + /* Fiddle around according to your need, (void *) alignment seems to be enough, + * but MAX_ALIGN_T is also an option. Especially for allocator implementation. */ + size_t size = align_up_to(itemsize, sizeof(void *)); + + if (size < sizeof(FreeListBlock) || !fl) + return 0; fl->start = start; - fl->end = end; + fl->end = start + size_bytes; fl->size = size; fl->allocated = 0; FreeListBlock *block = (FreeListBlock *)start; - for (size_t i = 0; i < fl_capacity(fl); i++) { - block->next = (FreeListBlock *)((void *)block + size); - block = block->next; + + for (size_t offset = 1; offset < fl_capacity(fl) + 1; offset++) { + block->next_offset = (int32_t)offset; + block = offset_to_ptr(fl, offset); } - block->next = NULL; - + block->next_offset = 0; /* Last block */ fl->free = (FreeListBlock *)start; - return EXIT_SUCCESS; + + return 1; } /* Allocate some memory from the FreeList */ @@ -41,26 +58,32 @@ void *fl_alloc(FreeList *fl) { FreeListBlock *m = fl->free; - fl->free = fl->free->next; + fl->free = offset_to_ptr(fl, fl->free->next_offset); /* May be null, which is fine */ fl->allocated++; - memset((void *)m, 0, sizeof(FreeListBlock)); + /* Wipe it before sending it out, could use memset + * here, or even wiping the entire block */ + m->next_offset = 0; + + /* For reference: */ + // memset((void *)m, 0, fl->size); /* Wipe entire block */ + // memset((void *)m, 0, sizeof(FreeListBlock)); /* Wipe only header */ + return ((void *)m); } /* Return some memory to the FreeList */ int fl_free(FreeList *fl, void *ptr) { - if (!fl_is_managed(fl, ptr)) { - return EXIT_FAILURE; /* We cant free memory we do not own */ - } + if (!fl_is_managed(fl, ptr)) + return 0; /* We cant free memory we do not own */ - FreeListBlock *block = (FreeListBlock *)(uintptr_t)ptr; + FreeListBlock *block = (FreeListBlock *)ptr; - block->next = fl->free; + block->next_offset = ptr_to_offset(fl, fl->free); /* May be null, which is fine */ fl->free = block; fl->allocated--; - return EXIT_SUCCESS; + return 1; } /* Returns how many slots are occupied */ @@ -80,7 +103,13 @@ size_t fl_capacity(FreeList *fl) { /* Check if a piece of memory is managed by the FreeList */ int fl_is_managed(FreeList *fl, void *ptr) { - return ((uintptr_t)ptr >= fl->start && (uintptr_t)ptr < fl->end); + uintptr_t p = (uintptr_t)ptr; + + if (p < fl->start || p >= fl->end) { + return 0; // outside pool + } + + return ((p - fl->start) % fl->size) == 0; // aligned to block } /* Returns the ratio of metadata versus data as a scalar in range 0..1 */ @@ -93,9 +122,16 @@ size_t fl_check(FreeList *fl) { int avail = 0; FreeListBlock *cursor = fl->free; - while (cursor->next != NULL) { + while (cursor) { avail++; - cursor = cursor->next; + + if (!fl_is_managed(fl, cursor) || avail > fl_capacity(fl)) + return 0; + + if (cursor->next_offset == 0) + break; + + cursor = offset_to_ptr(fl, cursor->next_offset); } return avail; diff --git a/freelist/freelist.h b/freelist/freelist.h index 015ee33..ed7949c 100644 --- a/freelist/freelist.h +++ b/freelist/freelist.h @@ -5,18 +5,6 @@ #include #include -/* Align to nearest multiple of align */ -static inline size_t align_up_to(size_t n, size_t align) { - return (n + align - 1) & ~(align - 1); -} - -/* Fiddle these around according to your need. Delete or check makefile */ -#ifdef FREELIST_ALIGN -#define ALIGN(x) (align_up_to(x, sizeof(void *))) -#else -#define ALIGN(x) (x) -#endif - typedef struct FreeListBlock FreeListBlock; typedef struct { @@ -27,7 +15,7 @@ typedef struct { size_t allocated; } FreeList; -int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize); +int fl_init(FreeList *fl, uintptr_t start, size_t size_bytes, size_t itemsize); int fl_free(FreeList *fl, void *ptr); int fl_is_managed(FreeList *fl, void *ptr); void *fl_alloc(FreeList *fl); diff --git a/freelist/hexdump.c b/freelist/hexdump.c new file mode 100644 index 0000000..cdf2121 --- /dev/null +++ b/freelist/hexdump.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +void hexdump(const void *data, size_t size) { + const unsigned char *p = (const unsigned char *)data; + size_t i, j; + + for (i = 0; i < size; i += 16) { + // Print offset + printf("%08zx ", i); + + // Print hex bytes + for (j = 0; j < 16; j++) { + if (i + j < size) { + printf("%02X ", p[i + j]); + } else { + printf(" "); // padding for incomplete lines + } + if (j == 7) + printf(" "); // extra space in middle + } + + printf(" |"); + + // Print ASCII characters + for (j = 0; j < 16 && i + j < size; j++) { + unsigned char c = p[i + j]; + printf("%c", isprint(c) ? c : '.'); + } + + printf("|\n"); + } +} diff --git a/freelist/hexdump.h b/freelist/hexdump.h new file mode 100644 index 0000000..d01a907 --- /dev/null +++ b/freelist/hexdump.h @@ -0,0 +1,8 @@ +#ifndef HEXDUMP_H +#define HEXDUMP_H + +#include + +void hexdump(const void *data, size_t size); + +#endif // HEXDUMP_H diff --git a/freelist/main.c b/freelist/main.c index 0fde246..ffb2068 100644 --- a/freelist/main.c +++ b/freelist/main.c @@ -1,4 +1,5 @@ #include "freelist.h" +#include "hexdump.h" #include #include #include @@ -17,31 +18,63 @@ void printvec(const Vec3 *v) { printf("Vec3: (%d, %d, %d)\n", v->x, v->y, v->z); } -#define BUFFER_SIZE (4096) +/* For testing crc poly 0x04C11DB7 */ +uint32_t crc32(const uint8_t *data, size_t length); + +#define BUFFER_SIZE (128) int main() { - FreeList fl; - const char *mem = malloc(BUFFER_SIZE); + FreeList fl; + + /* Here we split our malloc'ed memory into three parts, we pass one of + * them to our freelist, and keep the other two (head/tail) to check for + * overwrites: + * + * | Headblock | Freelist block | Tailblock | + * | 1x | 2x | 1x | + */ + void *headblock = malloc(BUFFER_SIZE * 2); + void *tailblock = headblock + (BUFFER_SIZE + BUFFER_SIZE / 2); + void *mem = headblock + (BUFFER_SIZE / 2); + + /* Set these buffers to something known and randomize the buffer we give to the freelist */ + memset(mem, arc4random(), BUFFER_SIZE); + memset(headblock, 0xAB, BUFFER_SIZE / 2); + memset(tailblock, 0xF0, BUFFER_SIZE / 2); + + /* Store these for reference at the end of the testing */ + const uint32_t tail_crc = crc32(tailblock, BUFFER_SIZE / 2); + const uint32_t head_crc = crc32(headblock, BUFFER_SIZE / 2); + + /* Display a nice hexdump so you can see the layout */ + hexdump(headblock, BUFFER_SIZE * 2); + + if (!fl_init(&fl, (uintptr_t)mem, BUFFER_SIZE, sizeof(Vec3))) { + printf("Freelist failed to initialize!\n"); + exit(EXIT_FAILURE); + } - fl_init(&fl, (uintptr_t)mem, (uintptr_t)mem + BUFFER_SIZE, sizeof(Vec3)); const size_t cap = fl_capacity(&fl); printf("Item size: %lu\n", sizeof(Vec3)); + printf("FreeList Capacity: %lu\n", cap); printf("Buffer size: %d\n", BUFFER_SIZE); + printf("Freelist Blocksize: %lu\n", fl.size); printf("Space utilization internal: %.2f%%\n", 100.0 * fl_utilization(&fl, sizeof(Vec3))); printf("Space utilization external: %.2f%%\n", 100.0 * ((float)fl.size * cap) / BUFFER_SIZE); assert(fl_available(&fl) == cap); + assert(fl_check(&fl) == cap); Vec3 *a = fl_alloc(&fl); Vec3 *b = fl_alloc(&fl); Vec3 *c = fl_alloc(&fl); - memset(a, 0x23, sizeof(Vec3)); - memset(b, 0x24, sizeof(Vec3)); - memset(c, 0x25, sizeof(Vec3)); + memset(a, 0xCA, sizeof(Vec3)); + memset(b, 0xCB, sizeof(Vec3)); + memset(c, 0xCC, sizeof(Vec3)); - *a = vec3_new(12, 13, 435); + *a = vec3_new(0xAAAA, 0xAAAA, 0xAAAA); *b = vec3_new(192, 199, 435); *c = vec3_new(432, 11, 435); @@ -54,9 +87,9 @@ int main() { printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); - assert(fl_free(&fl, a) == EXIT_SUCCESS); - assert(fl_free(&fl, b) == EXIT_SUCCESS); - assert(fl_free(&fl, c) == EXIT_SUCCESS); + assert(fl_free(&fl, a)); + assert(fl_free(&fl, b)); + assert(fl_free(&fl, c)); printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); @@ -72,6 +105,10 @@ int main() { ptr_buf[i] = (uintptr_t)fl_alloc(&fl); } + /* Write some data to the last element to catch out of bounds writes */ + size_t last_elem = fl_capacity(&fl) - 1; + memset((void *)ptr_buf[last_elem], 0x42, sizeof(Vec3)); + assert(0 == fl_available(&fl)); assert(fl_allocated(&fl) == fl_capacity(&fl)); assert(fl_check(&fl) == 0); @@ -81,10 +118,39 @@ int main() { fl_free(&fl, (void *)ptr_buf[i]); } + free(ptr_buf); + + /* All slots are free here */ assert(cap == fl_available(&fl)); assert(fl_allocated(&fl) == 0); assert(fl_check(&fl) == cap); + /* Check so our head and tail blocks are intact */ + assert(crc32(headblock, BUFFER_SIZE / 2) == head_crc); + assert(crc32(tailblock, BUFFER_SIZE / 2) == tail_crc); + + /* Hexdump the heap, free the block and declare victory */ + hexdump(headblock, BUFFER_SIZE * 2); + free(headblock); printf("All tests passed!\n"); return 0; } + +uint32_t crc32(const uint8_t *data, size_t length) { + uint32_t crc = 0xFFFFFFFF; + uint32_t poly = 0x04C11DB7; + + for (size_t i = 0; i < length; i++) { + crc ^= ((uint32_t)data[i]) << 24; + + for (int j = 0; j < 8; j++) { + if (crc & 0x80000000) { + crc = (crc << 1) ^ poly; + } else { + crc <<= 1; + } + } + } + + return crc ^ 0xFFFFFFFF; +}