Fix arena_tail_realloc memory bug

`arena_realloc_tail` exceeding PAGE_SIZE would create a big page,
preceeding calls to `arena_realloc_tail` would then cause memory bugs.
This commit is contained in:
Ole Morud
2023-06-04 19:34:53 +02:00
parent 0cde95de64
commit 1fd96ff1ce
2 changed files with 25 additions and 15 deletions

View File

@@ -16,7 +16,7 @@ dynamic : $(BIN_DIR)/libarena.so
tests : test/test_arena tests : test/test_arena
$(TEST_DIR)/test_arena : src/test_arena.c $(BIN_DIR)/libarena.a | $(TEST_DIR) $(TEST_DIR)/test_arena : src/test_arena.c $(BIN_DIR)/libarena.a | $(TEST_DIR)
$(CC) -o $@ -O0 -ggdb $^ $(CC) -o $@ -O0 -Iinclude -ggdb $^
$(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR) $(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR)
ar cr $@ $^ ar cr $@ $^

View File

@@ -11,6 +11,8 @@
// compilers it is) // compilers it is)
#define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE)) #define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE))
#define BIG_PAGE (SYS_PAGE_SIZE + 1)
typedef unsigned char byte_t; typedef unsigned char byte_t;
struct page { struct page {
@@ -24,7 +26,7 @@ struct page {
arena_t arena_new(void) arena_t arena_new(void)
{ {
struct page* p = malloc(sizeof(struct page)); struct page* p = malloc(sizeof *p);
if (p == NULL) if (p == NULL)
exit(errno); exit(errno);
@@ -47,6 +49,8 @@ arena_t arena_new(void)
void _arena_new_page(arena_t* a, size_t size) void _arena_new_page(arena_t* a, size_t size)
{ {
/* potentially reuse page from previously
reset arena */
if (a->head->next != NULL) { if (a->head->next != NULL) {
a->head = a->head->next; a->head = a->head->next;
a->head->offset = 0; a->head->offset = 0;
@@ -54,15 +58,12 @@ void _arena_new_page(arena_t* a, size_t size)
return; return;
} }
a->head->next = calloc(1, sizeof *(a->head)); a->head->next = calloc(1, sizeof *(a->head->next));
if (a->head->next == NULL) if (a->head->next == NULL)
exit(errno); exit(errno);
a->head = a->head->next; a->head = a->head->next;
a->head->offset = 0;
a->head->prev_offset = 0;
a->head->next = NULL;
a->head->data = malloc(size); a->head->data = malloc(size);
if (a->head->data == NULL) if (a->head->data == NULL)
@@ -79,40 +80,49 @@ void arena_reset(arena_t* a)
void* _arena_big_alloc(arena_t* a, size_t size) void* _arena_big_alloc(arena_t* a, size_t size)
{ {
_arena_new_page(a, size); _arena_new_page(a, size);
a->head->offset = SIZE_MAX; a->head->offset = BIG_PAGE;
a->head->prev_offset = SIZE_MAX; a->head->prev_offset = BIG_PAGE;
return a->head->data; return a->head->data;
} }
void* arena_alloc(arena_t* a, size_t size) void* arena_alloc(arena_t* a, size_t size)
{ {
// align size to machine word size
size = (size + _WORD_SIZE - 1) & ~(_WORD_SIZE - 1);
if (size > SYS_PAGE_SIZE) if (size > SYS_PAGE_SIZE)
return _arena_big_alloc(a, size); return _arena_big_alloc(a, size);
a->head->prev_offset = a->head->offset; // align size to machine word size
a->head->offset += size; size = (size + _WORD_SIZE - 1) & ~(_WORD_SIZE - 1);
if (a->head->offset > SYS_PAGE_SIZE) { if (a->head->offset > SYS_PAGE_SIZE - size) {
_arena_new_page(a, SYS_PAGE_SIZE); _arena_new_page(a, SYS_PAGE_SIZE);
return arena_alloc(a, size); return arena_alloc(a, size);
} }
a->head->prev_offset = a->head->offset;
a->head->offset += size;
return (byte_t*)(a->head->data) + a->head->prev_offset; return (byte_t*)(a->head->data) + a->head->prev_offset;
} }
void* arena_calloc(arena_t* a, size_t nmemb, size_t size) void* arena_calloc(arena_t* a, size_t nmemb, size_t size)
{ {
void* p = arena_alloc(a, nmemb * size); void* p = arena_alloc(a, nmemb * size);
memset((byte_t*)p, 0, size); memset(p, 0, nmemb * size);
return p; return p;
} }
void* arena_realloc_tail(arena_t* a, size_t len) void* arena_realloc_tail(arena_t* a, size_t len)
{ {
if (a->head->offset == BIG_PAGE) {
a->head->data = realloc(a->head->data, len);
if (a->head->data == NULL)
exit(errno);
return a->head->data;
}
a->head->offset = a->head->prev_offset; a->head->offset = a->head->prev_offset;
return arena_alloc(a, len); return arena_alloc(a, len);