I already know that there is no way to know if a pointer target still a valid allocation of it's already freed, so I'm trying to use pointer to pointer to solve this but it didn't work.
My objective is simply making print_block() detect if block pointer is Null or not.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void free_block(u_int8_t **_block) {
// Dereference
u_int8_t *block = *_block;
// Info
printf("free_block()\t-> %p Point To %p \n", &block, block);
// Free Block
free(block);
block = NULL;
}
void print_block(u_int8_t **_block) {
// Dereference
u_int8_t *block = *_block;
// Detectc if this block is freed
// This is the objective of this code
if(block == NULL) {
printf("print_block()\t-> %p Is Null.\n", block);
return;
}
// Info
printf("print_block()\t-> %p Point To %p -> ", &block, block);
// Print byte by byte
u_int8_t *p = block;
for(int i = 0; i < 3; i++) {
printf("0x%02X ", *(u_int8_t *)p);
p++;
}
printf("\n");
}
int main(void) {
// Allocat a block in the memory
u_int8_t *block = malloc(3 * sizeof(u_int8_t));
// Set all to zeros
memset(block, 0x00, 3);
// Info
printf("Main()\t\t\t-> %p Point To %p \n", &block, block);
// Print the block content
print_block(&block);
// Free the block
free_block(&block);
// Print the block content gain
// This shold print Null because
// we freed the block.
print_block(&block);
return 0;
}
Result
Main() -> 0x7fffd549cc58 Point To 0xfa42a0
print_block() -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0x00 0x00 0x00
free_block() -> 0x7fffd549cc60 Point To 0xfa42a0
print_block() -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0xA4 0x0F 0x00
First, you need to be aware that the function free(p) will just flag a block of memory to be available for any new allocation, that's it, nothing else; While your pointer p still Valid, and you can Read and Write using it even if the block is already freed.
About your question "How to detect if a block of memory is already freed?" the short answer in C is there is no standard way. But you can write your own pointer-tracker to detect if a block of memory is already freed, which is not hard to do, this is an example:
void *ptr_list[64];
int ptr_position = 0;
bool ptr_exist(void *p) {
if(p == NULL)
return false;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == p)
return true;
}
return false;
}
void ptr_add(void *p) {
if(p == NULL)
return;
if(!ptr_exist(p)) {
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == NULL) {
ptr_list[i] = p;
return;
}
}
ptr_list[ptr_position] = p;
ptr_position++;
}
}
void ptr_free(void **p) {
if(*p == NULL)
return;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == *p) {
ptr_list[i] = NULL;
}
}
for(int i = ptr_position; i >= 0; i--) {
if(ptr_list[i] == NULL) {
ptr_position = i;
break;
}
}
free(*p);
*p = NULL;
}
To use it, simply after you allocate a block of memory add your pointer to the tracker using ptr_add(), and when you want to free it, use ptr_free(). Finally you can check at any moment from any thread if this block of memory still valid or not using ptr_exist().
Full code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// -- Pointers Tracker --
void *ptr_list[64];
int ptr_position = 0;
bool ptr_exist(void *p) {
if(p == NULL)
return false;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == p)
return true;
}
return false;
}
void ptr_add(void *p) {
if(p == NULL)
return;
if(!ptr_exist(p)) {
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == NULL) {
ptr_list[i] = p;
return;
}
}
ptr_list[ptr_position] = p;
ptr_position++;
}
}
void ptr_free(void **p) {
if(*p == NULL)
return;
for(int i = 0; i < ptr_position; i++) {
if(ptr_list[i] == *p) {
ptr_list[i] = NULL;
}
}
for(int i = ptr_position; i >= 0; i--) {
if(ptr_list[i] == NULL) {
ptr_position = i;
break;
}
}
free(*p);
*p = NULL;
}
// ----------------------
void free_block(u_int8_t *block) {
// Info
printf("free_block()\t-> %p Point To %p \n", &block, block);
// Free Block
// free(block);
// block = NULL;
ptr_free((void *)&block);
}
void print_block(u_int8_t *block) {
// Detectc if this block is freed
// This is the objective of this code
if(!ptr_exist(block)) {
printf("print_block()\t-> %p Is Null.\n", block);
return;
}
// Info
printf("print_block()\t-> %p Point To %p -> ", &block, block);
// Print byte by byte
u_int8_t *p = block;
for(int i = 0; i < 3; i++) {
printf("0x%02X ", *(u_int8_t *)p);
p++;
}
printf("\n");
}
int main(void) {
// Allocat a block in the memory
u_int8_t *block = malloc(3 * sizeof(u_int8_t));
// Add it to the tracker
ptr_add((void *)block);
// Set all to zeros
memset(block, 0x00, 3);
// Info
printf("Main()\t\t\t-> %p Point To %p \n", &block, block);
// Print the block content
print_block(block);
// Free the block
free_block(block);
// Print the block content gain
// This shold print Null because
// we freed the block.
print_block(block);
return 0;
}
You can use a Block struct and a bit of discipline.
As an example
typedef struct
{
size_t size;
uint8_t fill;
uint8_t* data;
} Block;
Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void print_block(Block* block, const char* title);
and
make the functions operate on pointers to Block, encapsulating the data
make free_block() return NULL to ease the task of invalidating the block pointer
test the block address inside print_block()
use an optional title in print_block()
main as an example
int main(void)
{
const int test_size = 32;
Block* block = get_block(test_size, 0); // zero-filled
printf(
"Main()\t\t\t-> Pointer at %p points to data at "
"%p\n",
&block, block->data);
print_block(block, "\nBlock contents:");
block = free_block(block);
print_block(block, "\nafter free()");
return 0;
}
Maybe you find this way easier to read.
output of main as above
Main() -> Pointer at 00CFF754 points to data at 00E96C70
Block contents
print_block()-> valid block data of size 32 at 00E96C70
fill char is 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
after free()
print_block() -> block is null.
complete code
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
size_t size;
uint8_t fill;
uint8_t* data;
} Block;
Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void print_block(Block* block, const char* title);
int main(void)
{
const int test_size = 32;
Block* block = get_block(test_size, 0); // 3 bytes, zero filled
printf(
"Main()\t\t\t-> Pointer at %p points to data at "
"%p\n",
&block, block->data);
print_block(block, "\nBlock contents:");
block = free_block(block);
print_block(block, "\nafter free()");
return 0;
}
Block* free_block(Block* block)
{
if (block == NULL) return NULL;
if (block->data == NULL) return NULL;
free(block->data); // data is free
free(block);
return NULL;
}
Block* get_block(size_t size, uint8_t fill)
{
Block* block = malloc(sizeof(Block));
if (block == NULL) return NULL;
block->data = malloc(size * sizeof(uint8_t));
if (block->data == NULL) return NULL;
memset(block->data, fill, size);
block->fill = fill;
block->size = size;
return block;
}
void print_block(Block* block, char* title)
{
if (title != NULL) printf("%s\n", title);
if (block == NULL)
{
printf("print_block()\t-> block is null.\n");
return;
}
printf(
"print_block()->\tvalid block data of size %d at "
"%p\n\t\tfill char is %02X\n\n",
block->size, block->data, block->fill);
// Print byte by byte
const int nc = 16; // 16 bytes per line
size_t j = 1;
for (size_t i = 0; i < block->size; i += 1)
{
printf("%02X ", block->data[i]);
if (j >= nc)
printf("\n"), j = 1;
else
j += 1;
}
if ( j != 1 ) printf("\n");
return;
}
There is no standard way to tell if a pointer is valid, nor if it points to an allocated object nor if it was freed already.
Yet it is possible to design a framework on top of the allocation functions to keep track of all allocations and deallocations and maintain a list of valid pointers. Looking up a pointer value in this list will tell you if the pointer points to a valid allocated block and possibly what size was requested from the heap.
Here is a simplistic implementation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void track_add_pointer(void *p, size_t size);
int track_find_pointer(void *p, size_t *sizep);
void track_remove_pointer(void *p);
void *tmalloc(size_t size) {
void *p = (malloc)(size);
if (p) {
track_add_pointer(p, size);
}
return p;
}
void *tcalloc(size_t size, size_t nmemb) {
void *p = (calloc)(size, nmemb);
if (p) {
track_add_pointer(p, size * nmemb);
}
return p;
}
void *trealloc(void *p, size_t size) {
if (p) {
void *newp = NULL;
if (!track_find_pointer(p, NULL)) {
fprintf(stderr, "trealloc: invalid pointer %p\n", p);
} else {
if (size == 0) {
(free)(p);
} else {
newp = (realloc)(p, size);
}
if (newp != p) {
if (p) track_remove_pointer(p);
if (newp) track_add_pointer(newp);
}
}
return newp;
} else {
return tmalloc(size);
}
}
void tfree(void *p) {
if (p) {
if (!track_find_pointer(p, NULL)) {
fprintf(stderr, "tfree: invalid pointer %p\n", p);
} else {
(free)(p);
track_remove_pointer(p);
}
}
}
char *tstrdup(const char *s) {
char *p = NULL;
if (s) {
size_t len = strlen(s);
p = tmalloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
} else {
fprintf(stderr, "tstrdup: null pointer\n");
}
return p;
}
char *tstrndup(const char *s, size_t n) {
char *p = NULL;
if (s) {
size_t len = strnlen(s, n);
p = tmalloc(len + 1);
if (p) {
memcpy(p, s, len);
p[len] = '\0';
}
} else {
fprintf(stderr, "tstrndup: null pointer\n");
}
return p;
}
Related
Ive tried creating a dynamic database in which the user inputs the size of the database they want to create however im getting a memory leak after a certain amount of size inputs and im unsure what im doing incorrectly as im freeing everything as it should.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct _product_t
{
char *product;
float price;
} product_t;
product_t *newDatabase(product_t *database, int *dbSize, int newSize)
{
product_t *newdatabase = (product_t *)malloc(sizeof(*newdatabase) * newSize);
if (newSize < *dbSize)
{
for (int i = 0; i <= newSize; i++)
{
if (database[i].product != NULL)
{
free(database[i].product);
}
}
}
else
{
for (int i = 0; i <= *dbSize; i++)
{
if (database[i].product != NULL)
{
free(database[i].product);
}
}
}
for (int i = *dbSize; i < newSize; i++)
{
// newdatabase[i].product = (char*)malloc(sizeof(char)*100);
newdatabase[i].product = NULL;
newdatabase[i].price = -1;
}
*dbSize = newSize;
free(database);
return newdatabase;
}
int main(void)
{
product_t *database = NULL;
int dbSize = 0;
char cmd;
do
{
printf("Command?");
scanf(" %c", &cmd);
switch (cmd)
{
case 'q':
printf("Bye!");
break;
case 'n':
printf("Size? ");
int newSize2 = 0;
scanf("%d", &newSize2);
if (newSize2 < 0)
{
printf("Must be larger than 0");
break;
}
else
{
database = newDatabase(database, &dbSize, newSize2);
break;
}
default:
printf("Unkown command '%c'\n", cmd);
}
} while (cmd != 'q');
return 0;
}
#edit number 3
Ive opted on removing the malloc for the product pointer and decided to free all non NULL values with if and else statements
With the first malloc you allocate newSize pointers to product_t but you want to allocate an array of them instead:
product_t *newdatabase = malloc(newSize * sizeof *newdatabase);
You cannot deference the database pointer after you free it.
Why do you only free database[i].product for i >= newSize? Presumably you want to free all of them (but see realloc and code sample below).
Consider using realloc() to resize an array. If it was successful your old will be retained.
It's clumsy having client remember the previous database size, so consider creating a struct to old both the data and the size.
As you allocate fixed length string, it's easier to encode that in the type. Use constants (PRODUCT_LEN) instead of hard-coding magic values.
malloc() / realloc() of 0 bytes is implementation defined. My implementation returns a NULL so handling it as a special case.
malloc() does not guarantee initialized memory so either initialize it after or use calloc.
We don't cast void * (result from malloc) in C.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define PRODUCT_LEN 100
typedef struct {
size_t size;
char (*products)[PRODUCT_LEN];
float *prices;
} database;
database *newDatabase() {
return calloc(1, sizeof(database));
}
database *resizeDatabase(database *db, size_t newSize) {
if(!db) return NULL;
database tmp;
tmp.products = realloc(db->products, newSize * sizeof *db->products);
if(newSize && !tmp.products) {
printf("realloc of products failed\n");
// handle error
return NULL;
}
db->products = tmp.products;
tmp.prices = realloc(db->prices, newSize * sizeof *db->prices);
if(newSize && !tmp.prices) {
printf("realloc of prices failed\n");
// handle error; we successfully resize products but failed
// to resize price. So we may need to shrink products but
// what if that now fails?
return NULL;
}
db->prices = tmp.prices;
for(size_t i = db->size; i < newSize; i++) {
db->products[i][0] = '\0';
db->prices[i] = -1;
}
db->size = newSize;
return db;
}
void freeDatabase(database *db) {
if(!db) return;
free(db->products);
free(db->prices);
free(db);
}
int main(void) {
database *db = newDatabase();
size_t tests[] = { 0, 1, 2, 1, 0 };
size_t tests_len = sizeof tests / sizeof *tests;
for(size_t i = 0; i < tests_len; i++) {
database *newDb = resizeDatabase(db, tests[i]);
if(!newDb) {
printf("resizefailed %zu\n", tests[i]);
return 1;
}
db = newDb;
}
freeDatabase(db);
}
and here is the valgrind run:
==533946== HEAP SUMMARY:
==533946== in use at exit: 0 bytes in 0 blocks
==533946== total heap usage: 9 allocs, 9 frees, 440 bytes allocated
==533946==
==533946== All heap blocks were freed -- no leaks are possible
Newer version to compare to
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct _product_t {
char *product;
float price;
} product_t;
product_t *newDatabase(product_t *database, int *dbSize, int newSize) {
for (int i = 0; i < *dbSize; i++) {
if (database[i].product != NULL) {
free(database[i].product);
}
}
// Free the previous memory allocated for the database
// Free the previous memory allocated for the database
free(database);
// Allocate new space for newSize products on the heap
database = (product_t*) malloc(newSize * sizeof(product_t));
// Initialize all products in the database with NULL and -1
for (int i = 0; i < newSize; i++) {
database[i].product = NULL;
database[i].price = -1;
}
// Update the database size
*dbSize = newSize;
// Return the pointer to the new database
return database;
}
int main(void) {
product_t *database = NULL;
int dbSize = 0;
char cmd;
do{
printf("Command?");
scanf(" %c", &cmd);
switch (cmd) {
case 'q':
printf("Bye!");
break;
case 'n':
printf("Size? ");
int newSize2 = 0;
scanf("%d", &newSize2);
if(newSize2 < 0) {
printf("Must be larger than 0");
break;
}
else{
database = newDatabase(database, &dbSize, newSize2);
break;
}
default:
printf("Unkown command '%c'\n",cmd);
}
}while(cmd != 'q');
return 0;
}
I have an issue with memcpy and valgrind, telling me about an Invalid write of size 8.
I got to the point of figuring out where the faulty code is, but I have no clue as to why it is faulty...
I'm aware that there are other questions regarding that, but they don't help me really.
The following is an excerpt of the most important bits of my approach on a somewhat "universal" stack, when my regular value would be of type uintptr_t.
Here are two defines that I used below:
// default stack batch size
#define STACK_BATCH_DEFAULT 8
// size of one value in the stack
#define STACK_SIZEOF_ONE sizeof(uintptr_t)
The structure of the stack is as follows:
typedef struct Stack
{
size_t count; // count of values in the stack
size_t size; // size of one value in bytes
size_t alloced; // allocated count
uintptr_t *value; // the values
int batch; // memory gets allocated in those batches
}
Stack;
I have an initialization function for the stack:
bool stack_init(Stack *stack, size_t size, int batch)
{
if(!stack) return false;
stack->batch = batch ? batch : STACK_BATCH_DEFAULT;
stack->size = size;
stack->count = 0;
stack->value = 0;
stack->alloced = 0;
return true;
}
Then the stack_push function, where valgrind throws the error Invalid write of size 8:
bool stack_push(Stack *stack, uintptr_t *value)
{
if(!stack || !value) return false;
// calculate required amount of elements
size_t required = stack->batch * (stack->count / stack->batch + 1);
// allocate more memory if we need to
if(required > stack->alloced)
{
uintptr_t *tmp = realloc(stack->value, required * stack->size);
if(!tmp) return false;
stack->value = tmp;
stack->alloced = required;
}
// set the value
if(stack->size > STACK_SIZEOF_ONE)
{
memcpy(stack->value + stack->size * stack->count, value, stack->size); // <--- valgrind throws the error here
}
else
{
stack->value[stack->count] = *value;
}
// increment count
stack->count++;
return true;
}
Then in my program I'm calling the functions as follows:
Stack stack = {0};
stack_init(&stack, sizeof(SomeStruct), 0);
/* ... */
SomeStruct push = { // this is a struct that is larger than STACK_SIZEOF_ONE
.int_a = 0,
.int_b = 0,
.int_c = 0,
.id = 0,
.pt = pointer_to_struct, // it is a pointer to some other struct that was allocated beforehand
};
stack_push(&stack, (uintptr_t *)&push);
And with universal I meant that I can also have a regular stack:
Stack stack = {0};
stack_init(&stack, sizeof(uintptr_t), 0);
/* ... */
uintptr_t a = 100;
stack_push(&stack, &a);
Also, I'm open to hear general tips and advices if there are any things that should/could be improved :)
Edit: Below is a runnable code.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
// default stack batch size
#define STACK_BATCH_DEFAULT 8
// size of one value in the stack
#define STACK_SIZEOF_ONE sizeof(uintptr_t)
#define TESTCOUNT 10
#define MAX_BUF 16
typedef struct Stack
{
size_t count; // count of values in the stack
size_t size; // size of one value in bytes
size_t alloced; // allocated count
uintptr_t *value; // the values
int batch; // memory gets allocated in those batches
}
Stack;
typedef struct SomeStruct
{
size_t a;
size_t b;
size_t c;
size_t id;
char *str;
}
SomeStruct;
bool stack_init(Stack *stack, size_t size, int batch)
{
if(!stack) return false;
stack->batch = batch ? batch : STACK_BATCH_DEFAULT;
stack->size = size;
stack->count = 0;
stack->value = 0;
stack->alloced = 0;
return true;
}
bool stack_push(Stack *stack, uintptr_t *value)
{
if(!stack || !value) return false;
// calculate required amount of elements
size_t required = stack->batch * (stack->count / stack->batch + 1);
// allocate more memory if we need to
if(required > stack->alloced)
{
uintptr_t *tmp = realloc(stack->value, required * stack->size);
if(!tmp) return false;
stack->value = tmp;
stack->alloced = required;
}
// set the value
if(stack->size > STACK_SIZEOF_ONE)
{
memcpy(stack->value + stack->size * stack->count, value, stack->size); // <--- valgrind throws the error here
}
else
{
stack->value[stack->count] = *value;
}
// increment count
stack->count++;
return true;
}
bool stack_pop(Stack *stack, uintptr_t *value)
{
if(!stack) return false;
if(!stack->count) return false;
// decrement count of elements
stack->count--;
// return the value if we have an address
if(value)
{
if(stack->size > STACK_SIZEOF_ONE)
{
memcpy(value, stack->value + stack->size * stack->count, stack->size);
}
else
{
*value = stack->value[stack->count];
}
}
int required = stack->batch * (stack->count / stack->batch + 1);
if(required < stack->alloced)
{
uintptr_t *tmp = realloc(stack->value, required * stack->size);
if(!tmp) return false;
stack->value = tmp;
stack->alloced = required;
}
if(!stack->value) return false;
return true;
}
int main(void)
{
// initialize variables
bool valid = false;
Stack default_stack = {0};
Stack some_stack = {0};
// initialize stacks
stack_init(&default_stack, sizeof(uintptr_t), 0);
stack_init(&some_stack, sizeof(SomeStruct), 0);
// test default case - push
printf("Testing the default case, pushing...\n");
for(int i = 0; i < TESTCOUNT; i++)
{
uintptr_t push = i;
valid = stack_push(&default_stack, &push);
if(!valid) return -1;
}
// ...now pop
printf("Testing the default case, popping...\n");
do
{
uintptr_t pop = 0;
valid = stack_pop(&default_stack, &pop);
if(valid) printf("%llu,", pop);
}
while(valid);
printf("\n");
// test some case - push
printf("Testing some case, pushing...\n");
for(int i = 0; i < TESTCOUNT; i++)
{
// generate the push struct
SomeStruct push = {
.a = i * 10,
.b = i * 100,
.c = i * 1000,
.id = i,
.str = 0,
};
// allocate a string
push.str = malloc(MAX_BUF + 1);
snprintf(push.str, MAX_BUF, "%d", i);
// push
valid = stack_push(&some_stack, (uintptr_t *)&push);
if(!valid) return -1;
}
// ...now pop
printf("Testing some case, popping...\n");
do
{
SomeStruct pop = {0};
valid = stack_pop(&some_stack, (uintptr_t *)&pop);
if(valid)
{
printf("a=%d,b=%d,c=%d,id=%d,str=%s\n", pop.a, pop.b, pop.c, pop.id, pop.str);
free(pop.str);
}
}
while(valid);
printf("\n");
/* leave out free functions for this example.... */
return 0;
}
After hours I figured it out :D The mistake happened because I very rarely do pointer arithmetic... In short, I was assuming that it would always calculate with a byte.
Let's take a look at the lines containing:
memcpy(stack->value + stack->size * stack->count, value, stack->size);
...and break it down, so it is more readable. And also, I'll even add a handy dandy comment in it:
size_t offset = stack->size * stack->count; // offset in bytes
void *dest = stack->value + offset;
void *src = value;
memcpy(dest, src, stack->size);
Now the pro C-programmer should instantly spot the problem. It is with the calculation of stack->value + offset, where it should add offset in bytes but it is not, because the stack->value is of type uintptr_t * and not of type uint8_t *.
So to fix it, I replaced it with this line:
void *dest = (uint8_t *)stack->value + offset;
And the code works.
I've just encountered a very strange bug. I was doing unit-test for a simple function as below.
UPDATE: Thanks #Bodo, here's the minimal working example. You can simply compile and run tokenizer.c.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
/* ============================= BOOL =============================== */
#ifndef _BOOL_
#define _BOOL_
typedef enum {
true, false
} bool;
#endif // _BOOL_
/* ============================= STACK =============================== */
#ifndef _STACK_
#define _STACK_
typedef void (*stack_freefn)(void *elemAddr);
typedef struct {
size_t size; // number of element allowed
int ite; // point to the current last element
size_t elemSize; // size of each element (how many bytes)
void *elems; // stockage of elements
stack_freefn freefn; // free memory allocated for each element if necessary
} stack;
/* constructor */
void new_stack(stack *s, const size_t size, const size_t elemSize, stack_freefn freefn) {
s->size = size;
s->ite = 0;
s->elemSize = elemSize;
s->elems = malloc(size * elemSize);
s->freefn = freefn;
}
/* free memory */
void dispose_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
}
free(s->elems);
s->elems = NULL;
}
/* push one new element on the top */
void push_stack(stack *s, const void *value, const size_t elemSize) {
if (s->ite == s->size) {
s->size *= 2;
s->elems = realloc(s->elems, s->size * s->elemSize);
}
void *elemAddr = (char *)s->elems + s->elemSize * s->ite++;
memcpy(elemAddr, value, s->elemSize);
}
/* pop our the element on the top */
void pop_stack(stack *s, void *res) {
if (s->ite > 0) {
void *elemAddr = (char *)s->elems + ((s->ite - 1) * s->elemSize);
memcpy(res, elemAddr, s->elemSize);
s->ite--;
}
}
void clear_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
} else {
s->ite = 0;
}
}
size_t stack_size(stack *s) {
return s->ite;
}
#endif // _STACK_
/* ============================= VECTOR =============================== */
#ifndef _VECTOR_
#define _VECTOR_
typedef int (*VectorCompareFunction)(const void *elemAddr1, const void *elemAddr2);
typedef void (*VectorFreeFunction)(void *elemAddr);
typedef struct {
int elemSize; //how many byte for each element
int elemNum; //number of current element in vector
int capacity; //maximum number of element vector can hold
void *elems; //pointer to data memory
VectorFreeFunction freefn; //pointer to the function used to free each element
} vector;
/**
* Reallocate a new memory of twice of original size
* return 1 if reallocation success, otherwise return -1.
*/
static void DoubleMemory(vector *v) {
void *tmp = realloc(v->elems, v->capacity * v->elemSize * 2);
assert(tmp != NULL);
v->elems = tmp;
v->capacity *= 2;
}
/**
* Constructor
*/
void VectorNew(vector *v, int elemSize, VectorFreeFunction freefn, int initialAllocation) {
v->elems = malloc(initialAllocation * elemSize);
assert(v->elems != NULL);
v->elemSize = elemSize;
v->elemNum = 0;
v->capacity = initialAllocation;
v->freefn = freefn;
}
/**
* Frees up all the memory of the specified vector.
*/
void VectorDispose(vector *v) {
if (v->freefn != NULL) {
for (; v->elemNum > 0; v->elemNum--) {
void *elemAddr = (char *)v->elems + (v->elemNum - 1) * v->elemSize;
v->freefn(elemAddr);
}
}
free(v->elems);
v->elems = NULL;
}
/**
* Returns the logical length of the vector.
*/
int VectorLength(const vector *v) {
return v->elemNum;
}
/**
* Appends a new element to the end of the specified vector.
*/
void VectorAppend(vector *v, const void *elemAddr) {
/* double size if neccessary */
if (v->elemNum == v->capacity) DoubleMemory(v);
memcpy((char *)v->elems + v->elemNum * v->elemSize, elemAddr, v->elemSize);
v->elemNum++;
}
/**
* Search the specified vector for an element whose contents match the element passed as the key.
*/
int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchfn, int startIndex, bool isSorted) {
assert(key && searchfn);
if (v->elemNum == 0) return -1;
assert(startIndex >= 0 && startIndex < v->elemNum);
if (isSorted == true) {
/* binary search */
void *startAddr = (char *)v->elems + startIndex * v->elemSize;
int size = v->elemNum - startIndex;
void *resAddr = bsearch(key, startAddr, size, v->elemSize, searchfn);
return (resAddr != NULL)? ((char *)resAddr - (char *)v->elems) / v->elemSize : -1;
} else {
/* linear search */
for (int i = 0; i < v->elemNum; i++) {
if (searchfn((char *)v->elems + i * v->elemSize, key) == 0) {
return i;
}
}
return -1;
}
}
#endif // _VECTOR_
/* ============================= TOKENIZER =============================== */
/**
* Dump current string into vector as a new word.
* Strings are null-terminated.
*/
static void dumpstack(stack *s, vector *v) {
size_t len = stack_size(s);
char *word = (char *)malloc((len + 1) * sizeof(char)); // +1 for null-terminator
for (int i = len - 1; i >= 0; i--) {
pop_stack(s, word + i * sizeof(char));
}
word[len] = '\0';
VectorAppend(v, &word);
clear_stack(s);
}
static const size_t kTokenStackDefaultSize = 64;
static void tokenize(vector *words, char *stream) {
stack s;
new_stack(&s, kTokenStackDefaultSize, sizeof(char), NULL);
size_t len = strlen(stream);
bool begin = false;
char c;
for (int i = 0; i < len; i++) {
c = stream[i];
/* =============================== My printf() is here ============================== */
// printf("char c = [%c]\n", c);
/* =============================== My printf() is here ============================== */
if (isalpha(c) || isdigit(c)) {
if (begin == false) begin = true;
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
} else if (c == '-') {
if (begin == true) { // case: covid-19
push_stack(&s, &c, sizeof(char));
} else {
if (i < len - 1 && isdigit(stream[i + 1])) { // case: -9
begin = true;
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
} else if (c == '.' && begin == true) { // case: 3.14
if (isdigit(stream[i - 1])) {
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
dispose_stack(&s);
}
/* ============================= UNIT-TEST =============================== */
/**
* HashSetFreeFunction<char *>
*/
static void freestr(void *elemAddr) {
char *str = *(char **)elemAddr;
free(str);
}
/**
* HashSetCompareFunction<char *>
*/
static int compstr(const void *elemAddr1, const void *elemAddr2) {
char *str1 = *(char **)elemAddr1;
char *str2 = *(char **)elemAddr2;
return strcmp(str1, str2);
}
static void test_tokenize(void) {
printf("Testing Tokenizer.c::tokenize() ...\n");
char *sentence = "Covid-19: Top adviser warns France at 'emergency' virus moment - BBC News\nPi = 3.14\n-1 is negative.";
vector words;
VectorNew(&words, sizeof(char *), freestr, 256);
tokenize(&words, sentence);
char *musthave[] = {"covid-19", "top", "3.14", "-1"};
char *musthavenot[] = {"-", "1"};
assert(VectorLength(&words) == 16);
for (int i = 0; i < sizeof(musthave)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthave[i], compstr, 0, false) != -1);
}
for (int i = 0; i < sizeof(musthavenot)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthavenot[i], compstr, 0, false) == -1);
}
VectorDispose(&words);
printf("[ALL PASS]\n");
}
int main(void) {
test_tokenize();
}
I've got segmentation fault at first.
[1] 4685 segmentation fault testtokenizer
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
Just recall that before this test, I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault? Thx bros.
UPDATE:
Now I can't even reproduce this bug myself. tokenizer.c above can pass the unit-test. I thought it might caused by makefile prerequisite rules. gcc didn't re-compile some object files when source code is changed.
Thanks #Steve Summit, you make it clear that unfreed memory will not cause segmentation fault.
Thanks #schwern for code review, it's really helpful.
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
They call it undefined behavior, because its behavior is undefined. Seemingly unrelated operations might nudge things just a bit to make the code "work" but they're only tangentially related to the problem.
I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault?
No. It does mean the memory is unreferencable and "leaked". The memory will be freed to the operating system when the process exits.
The problem must lie elsewhere. Without seeing your whole program we can't say for sure, but two fishy things stand out.
You're defining a fixed sized stack, but you're pushing onto it an indeterminate number of times. Unless push_stack has protection against this, you will walk off your allocated memory.
You're storing references to variables on the stack. lower, c
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
Once lower goes out of scope it will automatically be freed and the memory reused. &lower is invalid once tokenize returns. This seems to be fine if your stack only lasts the length of the function, but it's worth noting.
And it's possible new_stack, push_stack, or dumpstack are doing something incorrect.
I'm doing a custom malloc. I did a very simple one but now I'm trying to merge and split blocks in order to improve the efficiency of calls to sbrk(). when I try to execute a custom program with not many mallocs it works perfectly. But as soon as I try more mallocs or for example the command ls after some successful allocations, it ends giving a weird segmentation fault (core dumped) when calling the split function.
Any help or hint would be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "struct.h"
static p_meta_data first_element = NULL;
static p_meta_data last_element = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define ALIGN8(x) (((((x)-1)>>3)<<3)+8)
#define MAGIC 0x87654321
void *malloc(size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes);
p_meta_data request_space(size_t size_bytes);
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2);
void split(p_meta_data meta_data, size_t size_bytes);
void free(void *ptr);
void *calloc(size_t num_bytes, size_t num_blocs);
void *realloc(void *ptr, size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes) {
p_meta_data current = first_element;
while (current && !(current->available && current->size_bytes >= size_bytes)){
fprintf(stderr, " %zu libre %d\n", current->size_bytes, current->available);
current = current->next;
}
if (current == NULL) {
fprintf(stderr, "null\n" );
} else {
fprintf(stderr, "%zu libre %d\n", current->size_bytes, current->available);
}
return current;
}
p_meta_data request_space(size_t size_bytes) {
if (size_bytes < 122880) {
size_bytes = 122880;
fprintf(stderr, "request %zu\n", size_bytes);
}
p_meta_data meta_data;
meta_data = (void *)sbrk(0);
if (sbrk(SIZE_META_DATA + size_bytes) == (void *)-1)
return (NULL);
meta_data->size_bytes = size_bytes;
meta_data->available = 0;
meta_data->magic = MAGIC;
meta_data->next = NULL;
meta_data->previous = NULL;
return meta_data;
}
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2) {
if (!meta_data1 || !meta_data2) {
return NULL;
}
meta_data1->size_bytes = meta_data1->size_bytes + SIZE_META_DATA + meta_data2->size_bytes;
meta_data1->next = meta_data2->next;
if (last_element == meta_data2) {
fprintf(stderr, "gleich\n");
last_element = meta_data1;
}
meta_data2 = NULL;
return meta_data1;
}
void free(void *ptr) {
p_meta_data meta_data;
if (!ptr)
return;
pthread_mutex_lock(&mutex);
meta_data = (p_meta_data)(ptr - SIZE_META_DATA);
if (meta_data->magic != MAGIC) {
fprintf(stderr, "ERROR free: value of magic not valid\n");
exit(1);
}
meta_data->available = 1;
fprintf(stderr, "Free at %x: %zu bytes\n", meta_data, meta_data->size_bytes);
p_meta_data meta_data_prev, meta_data_next;
meta_data_prev = meta_data->previous;
meta_data_next = meta_data->next;
if (meta_data_prev && meta_data_prev->available) {
meta_data = merge(meta_data_prev, meta_data);
}
if (meta_data_next && meta_data_next->available) {
meta_data = merge(meta_data, meta_data_next);
}
pthread_mutex_unlock(&mutex);
}
void split(p_meta_data meta_data, size_t size_bytes) {
if (!meta_data) {
fprintf(stderr, "no deberia entrar\n");
return;
}
p_meta_data meta_data2;
size_t offset = SIZE_META_DATA + size_bytes;
meta_data2 = (p_meta_data)(meta_data + offset);
fprintf(stderr, "size of metadata %d", meta_data->size_bytes - size_bytes - SIZE_META_DATA);
meta_data2->size_bytes = meta_data->size_bytes - size_bytes - SIZE_META_DATA;
meta_data2->available = 1;
meta_data2->magic = MAGIC;
meta_data2->previous = meta_data;
meta_data2->next = meta_data->next;
if (meta_data == last_element) {
last_element = meta_data2;
}
meta_data->size_bytes = size_bytes;
meta_data->next = meta_data2;
return;
}
void *malloc(size_t size_bytes) {
void *p, *ptr;
p_meta_data meta_data;
if (size_bytes <= 0) {
return NULL;
}
size_bytes = ALIGN8(size_bytes);
fprintf(stderr, "Malloc %zu bytes\n", size_bytes);
// Bloquegem perque nomes hi pugui entrar un fil
pthread_mutex_lock(&mutex);
meta_data = search_available_space(size_bytes);
if (meta_data) { // free block found
fprintf(stderr, "FREE BLOCK FOUND---------------------------------------------------\n");
meta_data->available = 0; //reservamos el bloque
} else { // no free block found
meta_data = request_space(size_bytes); //pedimos más espacio del sistema
if (!meta_data) //si meta_data es NULL (es decir, sbrk ha fallado)
return (NULL);
if (last_element) // we add the new block after the last element of the list
last_element->next = meta_data;
meta_data->previous = last_element;
last_element = meta_data;
if (first_element == NULL) // Is this the first element ?
first_element = meta_data;
}
fprintf(stderr, "die differenz %zu\n", meta_data->size_bytes - size_bytes);
if ((meta_data->size_bytes - size_bytes) > 12288) {
split(meta_data, size_bytes);
fprintf(stderr,"call split\n");
}
p = (void *)meta_data;
// Desbloquegem aqui perque altres fils puguin entrar
// a la funcio
pthread_mutex_unlock(&mutex);
// Retornem a l'usuari l'espai que podra fer servir.
ptr = p + SIZE_META_DATA; //p es puntero al inicio de meta_data, y ptr es el puntero al inicio del bloque de datos en sí (justo después de los metadatos)
return ptr;
}
void *calloc(size_t num_bytes, size_t num_blocs) {
size_t mem_to_get = num_bytes * num_blocs;
void *ptr = malloc(mem_to_get);
if (ptr == NULL) {
return ptr;
} else {
memset(ptr, 0, mem_to_get);
return ptr;
}
}
void *realloc(void *ptr, size_t size_bytes) {
fprintf(stderr, "realloc\n");
if (ptr == NULL) {
return malloc(size_bytes);
} else {
p_meta_data inic_bloc = (p_meta_data )(ptr - SIZE_META_DATA);
if (inic_bloc->size_bytes >= size_bytes) {
return ptr;
} else {
void *new_p = malloc(size_bytes);
memcpy(new_p, ptr, inic_bloc->size_bytes);
inic_bloc->available = 1;
return new_p;
}
}
}
where struct.h is:
#include <stddef.h>
#include <unistd.h>
#define SIZE_META_DATA sizeof(struct m_meta_data)
typedef struct m_meta_data *p_meta_data;
/* This structure has a size multiple of 8 */
struct m_meta_data {
size_t size_bytes;
int available;
int magic;
p_meta_data next;
p_meta_data previous;
};
Here are some remarks about your code:
it is confusing for the reader to hide pointers behind typedefs. Why not define m_meta_data as a typedef for struct m_meta_data and use m_meta_data * everywhere?
are you sure sbrk() is properly defined? The cast (void *)sbrk(0) seems to indicate otherwise. sbrk() is declared in <unistd.h> on POSIX systems.
BUG in split(), the computation meta_data2 = (p_meta_data)(meta_data + offset); is incorrect. It should be:
meta_data2 = (p_meta_data)((unsigned char *)meta_data + offset);
you should define strdup() and strndup() as the definitions from the C library might not call your redefined malloc():
char *strdup(const char *s) {
size_t len = strlen(s);
char *p = malloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
return p;
}
char *strndup(const char *s, size_t n) {
size_t len;
char *p;
for (len = 0; len < n && s[n]; len++)
continue;
if ((p = malloc(len + 1)) != NULL) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
blocks allocated with malloc() should be aligned on 16-byte boundaries on 64-bit intel systems. As a matter of fact, the m_meta_data structure has a size of 32 bytes on 64-bit systems, but 20 bytes on 32-bit systems. You should adjust your m_meta_data structure for 32-bit systems.
you should check for overflow in size_t mem_to_get = num_bytes * num_blocs;
you should not rely on void * arithmetics, it is a gcc extension. Write this instead:
p_meta_data inic_bloc = (p_meta_data)ptr - 1;
in realloc(), when extending the size of the block, you just make the original block available but you do not coalesce it with adjacent blocks as you do in free(). You might just call free(ptr), especially since modifying inic_bloc->available = 1; without getting the lock seems risky.
you should check meta_data->available in free() and realloc() to detect invalid calls and prevent arena corruption.
in malloc(), you forget to release the lock in case of allocation failure.
calling fprintf while the lock is set is risky: if fprintf calls malloc, you would get a deadlock. You might assume that printing to stderr does not call malloc() because stderr is unbuffered, but you are taking chances.
when allocating a new block with sbrk(), you should use sbrk(0) after the allocation to determine the actual size made available as it may have been rounded up to a multiple of PAGE_SIZE.
you should split blocks if (meta_data->size_bytes - size_bytes) > SIZE_META_DATA. The current test is far too loose.
I'm writing a program that needs to call a function that adds numbers until -1 is introduced, the problem is that after 3 numbers the program stops and gives segmentation fault:
int leNumeros(int **lista, int *nElem, int *tam)
{
int op, *temp = NULL;
*lista = (int*) malloc(*tam * sizeof(int));
if(*lista == NULL)
{
printf("memory fail\n");
} else
{
do
{
printf("number:\n");
scanf("%d",&op);
if(op >= 0)
{
if(*nElem >= *tam)
{
*lista = (int*) realloc( *lista, *nElem * sizeof(int) );
if(*lista == NULL)
{
printf("memory fail");
}
else
{
printf("added: %d bytes total: %d bytes\n",
*nElem * sizeof(int), *nElem * sizeof(int) + *tam * sizeof(int));
//*lista = temp;
}
}
*lista[*nElem] = op;
(*nElem)++;
}
}while(op >= 0);
}
}
int main(int argc, char** argv) {
int *lista = NULL, nElem = 0, tam = 0;
leNumeros(&lista, &nElem, &tam);
return (EXIT_SUCCESS);
}
I'm having trouble understanding what is going on, can anyone help me please?
When leNumeros() is called you attempt to allocate a zero length block - that has implementation defined behaviour.
Then when you enter a number > 0, you attempt to realloc() a zero length block - that behaviour is well defined it frees the original block then returns a null pointer, then at *lista[*nElem] = op; you deference that null pointer rather than aborting the loop.
In any event *lista[*nElem] = op; should be (*lista)[*nElem] = op;
Even if *nElem was non-zero, the line:
*lista = (int*)realloc(*lista, *nElem * sizeof(int));
is bad-practice, because if the reallocation fails the original block will leak because *lista will be come NULL without releasing whatever it previously pointed to. Instead you should (for example):
int* new_block = realloc(*lista, *nElem * sizeof(int));
if( new_block == NULL )
{
printf( "memory fail\n" ) ;
break ;
}
*lista = new_block ;
To work at all in main tam must be > 0, and to avoid possible failure due to implementation defined behaviour nElem should also be grater than zero.
I think the tam parameter is probably the block size to allocate. So for example, if tam = 10, allocate space for 10 ints. Then after taking 10 ints, realloc for 10 more.
Also, note that because of operator precedence, *lista[*nElem] = op; is the same as *(lista[*nElem]) = op;. You want to deference the pointer first, then use the brackets: (*lista)[*nElem] = op;
void leNumeros(int **lista, int *nElem, int *tam)
{
if (*tam <= 0) {
puts("Error: tam must be > 0");
return;
}
int op;
// Keep track of how many ints the array can hold
int items_allocated = *tam;
*lista = malloc(*tam * sizeof(int));
if (*lista == NULL)
{
printf("memory fail\n");
}
else
{
do
{
printf("number:\n");
if (1 != scanf("%d", &op)) break;
if (op >= 0) {
// Realloc a new block of tam bytes if out of space
if (*nElem >= items_allocated) {
items_allocated += *tam;
int *temp = realloc(*lista, items_allocated * sizeof(int));
if (temp == NULL) {
printf("memory fail");
break;
}
else {
*lista = temp;
printf("added: %zu bytes total: %zu bytes\n", *tam * sizeof(int), items_allocated * sizeof(int));
}
}
(*lista)[*nElem] = op;
(*nElem)++;
}
} while (op >= 0);
}
}
int main(int argc, char** argv) {
int *lista = NULL, nElem = 0, tam = 5;
leNumeros(&lista, &nElem, &tam);
for (int i = 0; i < nElem; i++) {
printf("%d ", lista[i]);
}
return (EXIT_SUCCESS);
}