I have school project to create memory allocator. I´ve been debugging it over 3 days now with no reasonable outcome. It is suddenly failing when higher number is ment to be allocated for example i have an array of 65000Bytes and I want to allocate 12000, it fails. Lower numbers up to 800 work great tho.
Let´s say we have static array consisting of 65000Bytes.
We call init, which creates linked list which has pointer to next list and size of list. We also are allowed to have one global variable for pointer to start of array I named mine "start".
Looks like this:
typedef struct mallocstr head;
char* start;
struct mallocstr
{
head* next; //For moving through list
int size; //If allocated then value is <0
};
And here is my allocator function:
void* memory_alloc(unsigned int size)
{
head* p = start;
char* endd= end();
while (p != NULL)
{
if (p->size > 0 && p->size >= size + sizeof(p) && size != 0)
{
int newsize = p->size - size - sizeof(p);
//If head is smaller than newsize create next block and allocate this one
if (sizeof(p) < newsize)
{
hlavicka* var;
p->size = (p->size - newsize) * (-1);
int point = p->size;
point*= -1;
p->next= p + sizeof(p) + point;
var= p;
p = p->next;
p->size= newsize;
p->next = NULL;
return (void*)var + 2*(sizeof(int));
}
//If head is greater or same as newsize
else if (sizeof(p) >= newsize)
{
p->size= p->size* (-1);
p->next = NULL;
return (void*)p + 2*(sizeof(int));
}
}
//If we are unable to alocate this block because size is too small or it is allocated we move to
//next one
else if (p->size< size + sizeof(p) && p != endd|| p->size< 0)
{
p = p->next;
}
//When we cannot allocate any block
else if (p->size < size + sizeof(p) && p == endd|| size == 0)
{
return NULL;
}
}
}
Function end is for getting pointer to the end of list. Here it is:
void* end()
{
head* p = start;
while (p != NULL)
{
if (p->next== NULL)
{
break;
}
else
p = p->next;
}
return (void*)p;
}
Related
I am trying to implement buddy allocator in C for FreeRTOS.
I made a function buddy_free for memory management.
I am using struct _buddy_block and function for allocation and memory management, but things don't go well and I need your help.
Here's my sources and problems below:
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;
Allocation:
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;
if (block->is_free) {
return;
}
// 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 (block < allocator->freelist ||
block > (buddy_block_t *) ((uint8_t *) allocator->freelist + allocator->total_size) ||
buddy < allocator->freelist ||
buddy > (buddy_block_t *) ((uint8_t *) allocator->freelist + allocator->total_size)) {
// One of the blocks is outside of the memory region managed by the allocator, so they 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;
}
}
First problem is that in the line:
while (block != NULL && (block->size < size || !block->is_free))
I get Segmentation fault with test_buddy_alloc_insufficient_memory test:
// Test the behavior of the buddy_alloc function when it is unable to fulfill an allocation request due to insufficient free memory
void test_buddy_alloc_insufficient_memory() {
// Allocate all of the available memory
buddy_allocator_t allocator;
void *ptr = buddy_alloc(&allocator, allocator.total_size);
assert(ptr != NULL);
// Attempt to allocate more memory
ptr = buddy_alloc(&allocator, 1);
assert(ptr == NULL);
}
// Test the behavior of the buddy_alloc function when it is called with a size of 0
void test_buddy_alloc_size_zero() {
buddy_allocator_t allocator;
// Attempt to allocate a block of size 0
void *ptr = buddy_alloc(&allocator, 0);
assert(ptr == NULL);
}
Can someone help me to fix or improve my code?
You have UB (undefined behavior).
In your test_* functions, you have:
buddy_allocator_t allocator;
This is on the stack and is uninitialized.
You need:
buddy_allocator_t allocator = { 0 };
With -Wall, the compiler flags:
orig.c: In function ‘test_buddy_alloc_insufficient_memory’:
orig.c:114:14: warning: ‘allocator.total_size’ is used uninitialized in this function [-Wuninitialized]
void *ptr = buddy_alloc(&allocator, allocator.total_size);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Also, in buddy_alloc, the first thing it does is:
buddy_block_t *block = allocator->freelist;
while (block != NULL && (block->size < size || !block->is_free))
The block != NULL is insufficient. It does not guard against dereferencing a non-null but random/invalid pointer value.
UPDATE:
From the discussion and further review, I didn't see any code to set up the allocator struct with a valid block. So, the allocator started without any memory.
Normally, we'd call brk/sbrk to get more memory from the system, but, for testing, we can use a static/persistent fixed array.
Here is the restructured code:
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
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;
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;
if (block->is_free) {
return;
}
// 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 (block < allocator->freelist || block > (buddy_block_t *) ((uint8_t *) allocator->freelist + allocator->total_size) || buddy < allocator->freelist || buddy > (buddy_block_t *) ((uint8_t *) allocator->freelist + allocator->total_size)) {
// One of the blocks is outside of the memory region managed by the allocator, so they 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;
}
}
void
initme(buddy_allocator_t *ctl)
{
#if 0
static buddy_block_t block;
static char mem[1024 * 1024];
memset(&block,0,sizeof(block));
block.size = sizeof(mem);
block.is_free = 1;
ctl->total_size = block.size;
ctl->freelist = █
ctl->min_block_size = 128;
#else
static char mem[1024 * 1024];
buddy_block_t *block = (void *) mem;
memset(block,0,sizeof(*block));
block->size = sizeof(mem) - sizeof(*block);
block->is_free = 1;
ctl->total_size = block->size;
ctl->freelist = block;
ctl->min_block_size = 128;
#endif
}
// Test the behavior of the buddy_alloc function when it is unable to fulfill an allocation request due to insufficient free memory
void
test_buddy_alloc_insufficient_memory()
{
// Allocate all of the available memory
buddy_allocator_t allocator = { 0 };
initme(&allocator);
void *ptr = buddy_alloc(&allocator, allocator.total_size);
assert(ptr != NULL);
// Attempt to allocate more memory
ptr = buddy_alloc(&allocator, 1);
assert(ptr == NULL);
}
// Test the behavior of the buddy_alloc function when it is called with a size of 0
void
test_buddy_alloc_size_zero()
{
buddy_allocator_t allocator = { 0 };
initme(&allocator);
// Attempt to allocate a block of size 0
void *ptr = buddy_alloc(&allocator, 0);
assert(ptr == NULL);
}
int
main(void)
{
test_buddy_alloc_insufficient_memory();
test_buddy_alloc_size_zero();
return 0;
}
I am trying to add memory deallocations to old C code.
I have a hash table of custom objects (HASHREC). After analysis of current code and reading other SO questions, I know that I need to provide three levels of deallocations. Fist - word member, next HASHREC*, and then HASHREC**.
My version of free_table() function frees mentioned objects. Unfortunately, Valgrind still complains that some bytes are lost.
I am not able to provide full code, it will be too long, but I am presenting how HASHREC **vocab_hash is filled inside inithashtable() and hashinsert().
Could you give me a suggestion how should I fix free_table()?
typedef struct hashrec {
char *word;
long long count;
struct hashrec *next;
} HASHREC;
HASHREC ** inithashtable() {
int i;
HASHREC **ht;
ht = (HASHREC **) malloc( sizeof(HASHREC *) * TSIZE );
for (i = 0; i < TSIZE; i++) ht[i] = (HASHREC *) NULL;
return ht;
}
void hashinsert(HASHREC **ht, char *w) {
HASHREC *htmp, *hprv;
unsigned int hval = HASHFN(w, TSIZE, SEED);
for (hprv = NULL, htmp = ht[hval]; htmp != NULL && scmp(htmp->word, w) != 0; hprv = htmp, htmp = htmp->next);
if (htmp == NULL) {
htmp = (HASHREC *) malloc( sizeof(HASHREC) ); //<-------- problematic allocation (Valgrind note)
htmp->word = (char *) malloc( strlen(w) + 1 );
strcpy(htmp->word, w);
htmp->next = NULL;
if ( hprv==NULL ) ht[hval] = htmp;
else hprv->next = htmp;
}
else {/* new records are not moved to front */
htmp->count++;
if (hprv != NULL) { /* move to front on access */
hprv->next = htmp->next;
htmp->next = ht[hval];
ht[hval] = htmp;
}
}
return;
}
void free_table(HASHREC **ht) {
int i;
HASHREC* current;
HASHREC* tmp;
for (i = 0; i < TSIZE; i++){
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
}
free(ht[i]);
}
free(ht);
}
int main(int argc, char **argv) {
HASHREC **vocab_hash = inithashtable();
// ...
hashinsert(vocab_hash, w);
//....
free_table(vocab_hash);
return 0;
}
I assume the problem is here:
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
}
free(ht[i]);
You release the word but you don’t release tmp. After you release the first item in the linked list but not the others which causes a leak.
Free tmp in there and don’t free ht[i] after since it’s already freed here.
current = ht[i];
while(current != NULL) {
tmp = current;
current = current->next;
free(tmp->word);
free(tmp);
}
I'm writing a simple parser in C and I'm not sure which is the best way to pass results up my tree as it gets evaluated.
Here's my current code, the node struct and the walk function to evaluate the tree.
typedef struct node {
struct node* left;
struct node* right;
void* data;
Symbol type;
} node;
void* walk(node* n) {
if (n != NULL) {
if (n->type == plus) {
int x = 0;
int a = *(int*)walk(n->left);
int b = *(int*)walk(n->right);
x = a + b;
return &x;
} else if (n->type == number) {
return (int*)n->data;
}
}
return NULL;
}
From the code you can see when I add two numbers together I'm storing the result in a local variable and returning the address to that variable, I know this is undefined behaviour, so I thought about using malloc and changing my code to this:
int* x = malloc(1 * sizeof(int));
int a = *(int*)walk(n->left);
int b = *(int*)walk(n->right);
*x = a + b;
return x;
But the problem with this code is, I'm not sure what is the best way to free this memory I just malloc'd.
Should I walk the tree a second time and free all of the memory that way or is the a better way to free the memory when I'm done or is there a better way to propagate values through my tree?
No need to traverse the tree for second time. Notice that you do not need values of a and b after summing them into x. so you can free them after addition which is shown in #flu's answer. More over, you can do it without using extra memory for flag.
Note: this code will through runtime error for invalid input. to handle this errors check for NULL pointers before accessing a pointer.
void* walk(node* n) {
if (n != NULL) {
if (n->type == plus) {
int * x = malloc(sizeof(int));
int * a = (int*)walk(n->left);
int * b = (int*)walk(n->right);
*x = *a + *b;
free(a);
free(b);
return x;
} else if (n->type == number) {
int * val = malloc(sizeof(int)); //allocate dynamic memory for the leaf node so that all nodes can be freed without checking.
*val = n->data;
return val;
}
}
return NULL;
}
You could add an extra argument needToFree to inform the caller to free the returned pointer.
void* walk(node* n, bool* needToFree) {
if (n != NULL) {
if (n->type == plus) {
bool needToFreeA;
bool needToFreeB;
int * x = malloc(sizeof(int));
int * a = (int*)walk(n->left, &needToFreeA);
int * b = (int*)walk(n->right, &needToFreeB);
*x = *a + *b;
if( needToFreeA ) free(a);
if( needToFreeB ) free(b);
*needToFree = true;
return x;
} else if (n->type == number) {
*needToFree = false;
return (int*)n->data;
}
}
*needToFree = false;
return NULL;
}
I try to write my own custom malloc and free function in c. I worked around 12 hours on this and tried lots of things. But it doesn't work.
Maybe you guys can figure out the error. Allocated memory gets removed from the list with a next pointer to a specific address to identify it later in the free function. The current error is a segmentation fault 11 in the split method.
C-File:
Head:
#define MAGIC ((void*)0xbaadf00d)
#define SIZE (1024*1024*1)
typedef struct mblock {
struct mblock *next;
size_t size;
char memory[];
}mblock;
char memory[SIZE];
static struct mblock *head;
malloc:
void *halde_malloc (size_t size) {
printf("Starting\n");
printf("%zu\n",size);
if(size <= 0) {return NULL;}
if(head == NULL){
initializeBlock();
printf("Memory initialized\n");
}
mblock *temp_block = head;
while(temp_block != NULL) {
printf("IN\n");
if(temp_block->size == size) {
list_remove(temp_block);
temp_block->next = MAGIC;
return (void*)(temp_block);
} else if(temp_block->size > size) {
size_t temp_size = temp_block->size;
printf("size IS more than equal\n");
list_split_AND_Remove(temp_size - size, temp_block);
temp_block->size = size;
temp_block->next = MAGIC;
return (void*)(temp_block);
}
temp_block = temp_block->next;
printf("One block checked\n");
}
errno = ENOMEM;
return NULL;
}
Initialize:
void initializeBlock(){
printf("Initializing\n");
head = (mblock*)memory;
head->size=sizeof(memory)-sizeof(mblock);
head->next=NULL;
}
Split:
void list_split_AND_Remove(size_t size, mblock *lastBlock) {
printf("Split\n");
mblock *new = (void*)((mblock*)lastBlock+size+sizeof(mblock));
new->size = size - sizeof(mblock);
new->next = lastBlock->next;
lastBlock->next = new;
printf("START REMOVE");
list_remove(lastBlock);
}
Remove:
void list_remove(mblock *p) {
printf("Remove\n");
mblock *temp_block = head;
if(p == head) {
if(head->next == NULL) {
head = NULL;
return;
} else {
head = p->next;
return;
}
}
while(temp_block->next != NULL) {
if(temp_block->next == p) {
printf("Found P:");
temp_block = p->next;
return;
}
temp_block = temp_block->next;
}
}
Free:
void halde_free (void *ptr) {
printf("FREE\n");
mblock *new_block = ptr;
if(new_block->next == MAGIC) {
new_block->next = head;
head = new_block;
} else {abort();}
}
Issues with your code include, but are not necessarily limited to:
list_remove() does not actually remove the specified block from the list unless it happens to be the current list head. In every other case, therefore, halde_malloc() corrupts the list after calling list_remove() when it modifies the node's next pointer.
list_split_AND_Remove() performs incorrect pointer arithmetic. Specifically, mblock *new = (void*)((mblock*)lastBlock+size+sizeof(mblock)); does not do what you appear to want to do, because pointer arithmetic operates in units the size of the pointed-to type, whereas the size argument and the result of the sizeof operator have units of individual bytes. (Also, both casts are useless, albeit not harmful in themselves.)
Your allocator returns a pointer to the block header, not to its data. As a result, the user will very likely overwrite the block header's contents, leading to havoc when you later try to free that block.
You seem to assume that mblock objects have an alignment requirement of 1. That might not be true.
I'm creating a table with linked lists where the data is duplicated when it is passed to the insertion methods.
To test this, I create an array with the values that I will insert and then insert them into the table. When I free the array and then free the table, I receive a SEG FAULT error. As such, I have concluded that the pointers in both structures must be pointing to the same memory region. However, as I duplicate the data, I cannot see where the problem could be coming from...
Here is the code for the test:
for(i=0; i<1024; i++) {
key[i] = (char*)malloc(16*sizeof(char));
sprintf(key[i],"a/key/b-%d",i);
data[i] = data_create2(strlen(key[i])+1,strdup(key[i]));
table_put(table,key[i],data[i]);
}
assert(table_size(table) == 1024);
result = (table_size(table) == 1024);
for(i=0; i<1024; i++) {
d = table_get(table,key[i]);
assert(d->datasize == data[i]->datasize);
assert(memcmp(d->data,data[i]->data,d->datasize) == 0);
assert(d->data != data[i]->data);
result = result && (d->datasize == data[i]->datasize &&
memcmp(d->data,data[i]->data,d->datasize) == 0 &&
d->data != data[i]->data);
data_destroy(d);
}
for(i=0; i<1024; i++) {
free(key[i]);
//data_destroy(data[i]);
}
table_destroy(table);
When I uncomment that data_destroy(data[i]) line, the program gives the Seg Fault.
The code for the table_put:
int table_put(struct table_t *table, char * key, struct data_t *value) {
if(table == NULL || key == NULL || value == NULL) return -1;
struct entry_t *new_pair = entry_create(key, value);
int i = key_hash(key, table->size);
int l = 0;
if (list_get(table->list[i], new_pair->key) == NULL) {
l = 1;
}
if(list_add(table->list[i], new_pair)==-1){
entry_destroy(new_pair);
return -1;
}
table -> length = table -> length + l;
return 0;
}
The code for: entry_create, where I duplicate the data:
struct entry_t *entry_create(char *key, struct data_t *data){
if(data == NULL || key == NULL){
return NULL;
}
struct entry_t *entry = (struct entry_t *) malloc(sizeof(struct entry_t));
if(entry == NULL){
return NULL;
}
entry->key = (char*) malloc(sizeof(strlen(key))+1);
memcpy(entry->key,key,strlen(key)+1);
entry->value = data_dup(data);
//free(key);
data_destroy(data);
return entry;
}
segfaults on free are usually double free's means that you've tried to free something that's already been free'd . or when you try to free pointer with out any malloc.
is there any malloc on data_create2 function ?
if there is not it is the case you are trying to free data array members that where never malloc'd