I am trying to write Buddy memory allocator for FreeRTOS.
Structs:
typedef struct _buddy_block {
struct _buddy_block *next;
size_t size;
bool is_free;
} buddy_block_t;
typedef struct {
buddy_block_t *freelist;
size_t total_size;
size_t min_block_size;
} buddy_allocator_t;
Here's some of my code:
#include <stdint.h>
#include <cstring>
#include "buddy_alloc.h"
#define BUDDY_MIN_BLOCK_SIZE 32
#define NULL 0
void buddy_init(buddy_allocator_t *allocator, void *memory, size_t *size) {
// Initialize the allocator structure
allocator->total_size = *size;
allocator->min_block_size = BUDDY_MIN_BLOCK_SIZE;
allocator->freelist = (buddy_block_t *) memory;
allocator->freelist->next = NULL;
allocator->freelist->size = *size;
allocator->freelist->is_free = true;
}
void *buddy_alloc(buddy_allocator_t *allocator, size_t size) {
// Find the first free block that is large enough to satisfy the request
buddy_block_t *block = allocator->freelist;
while (block != NULL && (block->size < size || !block->is_free)) {
block = block->next;
}
// If no suitable block was found, return NULL
if (block == NULL) {
return NULL;
}
// Split the block into two blocks if the block is larger than needed
if (block->size > size) {
// Create a new block for the remainder
buddy_block_t *remainder = (buddy_block_t *) ((uint8_t *) block size);
remainder->size = block->size - size;
remainder->is_free = true;
remainder->next = block->next;
// Update the current block
block->size = size;
block->next = remainder;
}
// Mark the block as allocated and return a pointer to the memory
block->is_free = false;
return (void *) (block 1);
}
void buddy_free(buddy_allocator_t *allocator, void *ptr) {
// Get a pointer to the block header
buddy_block_t *block = (buddy_block_t *) ptr - 1;
// Mark the block as free
block->is_free = true;
// Try to merge the block with its buddy (if it has one and the buddy is free)
size_t block_size = block->size;
buddy_block_t *buddy = (buddy_block_t *) ((uint8_t *) block block_size);
// Check if the buddy block is within the memory region managed by the allocator
if (buddy < allocator->freelist ||
buddy > (buddy_block_t *) ((uint8_t *) allocator->freelist allocator->total_size)) {
// The buddy is outside of the memory region managed by the allocator, so it cannot be merged
return;
}
// Check if the buddy block is free and has the same size as the current block
if (buddy->is_free && buddy->size == block_size) {
// The buddy is free and has the same size as the current block, so they can be merged
if (buddy < block) {
// The buddy comes before the current block in memory, so it should be the new block
buddy->size *= 2;
buddy->next = block->next;
block = buddy;
} else {
// The current block comes before the buddy in memory, so it should be the new block
block->size *= 2;
block->next = buddy->next;
}
}
// Insert the merged block back into the free list
buddy_block_t *prev = NULL;
buddy_block_t *curr = allocator->freelist;
while (curr != NULL && curr < block) {
prev = curr;
curr = curr->next;
}
block->next = curr;
if (prev == NULL) {
allocator->freelist = block;
} else {
prev->next = block;
}
}
Now I need to test it, I wrote some tests in main.c:
// Test 1: Test with a single block in the free list
void test_buddy_init_1() {
// Initialize the allocator
buddy_allocator_t allocator;
size_t size = 128;
buddy_init(&allocator, NULL, &size);
// Check the total size of the memory region
assert(allocator.total_size == 16);
// Check the size of the first block in the free list
assert(allocator.freelist->size == 16);
// Check the "is_free" flag of the first block
assert(allocator.freelist->is_free == true);
// Check the "next" pointer of the first block
assert(allocator.freelist->next == NULL);
}
// Test 2: Test with a larger memory region
void test_buddy_init_2() {
// Initialize the allocator
buddy_allocator_t allocator;
size_t size = 128;
buddy_init(&allocator, NULL, &size);
// Check the total size of the memory region
assert(allocator.total_size == 128);
// Check the size of the first block in the free list
assert(allocator.freelist->size == 128);
// Check the "is_free" flag of the first block
assert(allocator.freelist->is_free == true);
// Check the "next" pointer of the first block
assert(allocator.freelist->next == NULL);
}
When I run these tests I get Segmentation Fault - Process finished with exit code -1073741819 (0xC0000005)
So, my question is, what's the issue? It's because I run these tests on Windows? Or am I doing something wrong in the code?
CodePudding user response:
There's a NULL pointer dereference in buddy_init
in your tests.
void test_buddy_init_2() {
// Initialize the allocator
buddy_allocator_t allocator;
size_t size = 128;
buddy_init(&allocator, NULL, &size);
...
void buddy_init(buddy_allocator_t *allocator, void *memory, size_t *size) {
// Initialize the allocator structure
allocator->total_size = *size;
allocator->min_block_size = BUDDY_MIN_BLOCK_SIZE;
allocator->freelist = (buddy_block_t *) memory;
allocator->freelist->next = NULL;
...
As can be seen memory
is NULL, therefore allocator->freelist
is NULL, therefore allocator->freelist->next
is a NULL pointer dereference and therefore undefined behaviour. This is the cause of your crash.
I'm guessing that you should pass a suitably large block of memory to buddy_init
instead of NULL.