I'm attempting to develop a library in C & starting with memory management.
Attempting to test my allocation function & free function several times to test for multiple allocations & frees.
However, on the 2nd run of free function I'm getting an double free & crash.
My simple header:
#include <stdio.h>
#include <stdlib.h>
#include "xyz_props.h"
#include <assert.h>
#include <string.h>
Header:
#ifndef XYZ_PROPS_H
#define XYZ_PROPS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#define DEFAULT_MAX_KEY_SIZE_IN_BYTES 256
#define DEFAULT_MAX_VALUE_SIZE_IN_BYTES 4096
/*represents a single linked list node with (key,value) pair*/
typedef struct xyz_config_node
{
char* key;
char* value;
struct xyz_config_node* p_next;
} xyz_config_node;
/*represents all properties*/
typedef struct xyz_config_list
{
xyz_config_node* p_head;
int KEY_SIZE_IN_BYTES;
int VALUE_SIZE_IN_BYTES;
} xyz_config_list;
/*declare variables*/
extern xyz_config_list* p_self;
/*===========================================================================
Function: xyz_config_alloc
Description: allocates heap memory for the wrapper xyz_config_list
* that contains the head node.
Inputs: max_key_size in bytes.
* Input of 0 or greater than 4096 will default to 256 bytes
* length for the key size.
*
* max_value_size in bytes.
* Input of 0 or greater than 4096 will default to 4096 bytes
* length for the value size.
Outputs: pointer to xyz_config_list
==========================================================*/
xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size);
/*===========================================================================
Function: xyz_config_free
Description: Frees heap memory allocated to xyz_config_list & the
* linked list within xyz_config_list.
Inputs: xyz_config_list** pp_self - pass by reference
Outputs: void
References:
Example call: xyz_config_free(&props);
==========================================================*/
void xyz_config_free(xyz_config_list** pp_self);
Implementation C file:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "xyz_props.h"
xyz_config_list* p_self = NULL;
/*=============================================================================
Private Declarations
==============================================================================*/
xyz_config_list* xyz_config_alloc_helper(int max_key_size, int max_value_size, xyz_config_list** props);
/*=============================================================================
* Implementations
==============================================================================*/
xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size) {
return xyz_config_alloc_helper(max_key_size, max_value_size,&p_self);
}
xyz_config_list* xyz_config_alloc_helper(int max_key_size, int max_value_size, xyz_config_list** props)
{
if (NULL == *props) {
*props = (xyz_config_list*) calloc(1, sizeof(xyz_config_list));
//set max key size
if (max_key_size > 0 && max_key_size<=4096) {
(*props)->KEY_SIZE_IN_BYTES = max_key_size;
} else {
//defaults to 256
(*props)->KEY_SIZE_IN_BYTES = DEFAULT_MAX_KEY_SIZE_IN_BYTES;
fprintf(stderr,"WARNING xyz_config,xyz_config_alloc_helper(), "
"max_key_size MUST be 0<max_key_size<=4096.max_key_size is set to "
"default 256.\n");
}
//set max value size
if (max_value_size > 0 && max_value_size<=4096) {
(*props)->VALUE_SIZE_IN_BYTES = max_value_size;
} else {
//defaults to 4096
(*props)->VALUE_SIZE_IN_BYTES = DEFAULT_MAX_VALUE_SIZE_IN_BYTES;
fprintf(stderr,"WARNING xyz_config,xyz_config_alloc_helper(), "
"max_value_size MUST be 0<max_value_size<=4096.max_value_size is set to "
"default 4096.\n");
}
}
return *props;
}
void xyz_config_free(xyz_config_list** pp_self)
{
if (NULL!=pp_self && NULL!=(*pp_self))
{
xyz_config_node* p_current = (*pp_self)->p_head;
xyz_config_node* p_next = NULL;
//iterate and free the nodes
while (NULL!=p_current)
{
p_next = p_current->p_next;
//free child attributes
free(p_current->key);
free(p_current->value);
//free the node
free(p_current);
p_current = p_next;
}
//free the super structure
if (NULL!=*pp_self)
{
free (*pp_self); //ERROR HAPPENS ON 2ND TIME HERE.
*pp_self = NULL;
}
}
}
Main file:
/*
*
*/
void test();
void test2();
int main(int argc, char** argv) {
test();
return (EXIT_SUCCESS);
}
/*single alloc & free*/
void test()
{
xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096
assert(props);
//finally free all memory
xyz_config_free(&props);
assert(NULL==props);
printf("free\n");
}
/*multiple allocs & frees*/
void test2()
{
//1-alloc
xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096
assert(props);
//1-finally free all memory
xyz_config_free(&props);
assert(NULL==props);
//2- alloc
props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096
assert(props);
//2-finally free all memory
xyz_config_free(&props); //CRASH in 2nd free function. Output: RUN FINISHED; Segmentation fault; core dumped;
assert(NULL==props);
printf("free\n");
}
The debugger memory & variables watch at the 1st run:
The debugger memory & variables watch at the 2nd run where there is a problem:
Any help will be appreciated.
CodePudding user response:
It seems the problem is relative to declaring the variable p_self
in the file scope
xyz_config_list* p_self = NULL;
This variable is used in the function xyz_config_alloc_helper
. The memory is allocated only in case when the file scope variable p_self
is equal to NULL
.
xyz_config_list* xyz_config_alloc(int max_key_size, int max_value_size) {
return xyz_config_alloc_helper(max_key_size, max_value_size,&p_self);
^^^^^^^
}
and (within the function xyz_config_alloc_helper
)
if (NULL == *props) {
*props = (xyz_config_list*) calloc(1, sizeof(xyz_config_list));
However in the function xyz_config_free
the variable is not set to NULL
because the function deals with the local variable declared in functions test and test2 as for example
xyz_config_list* props = xyz_config_alloc(128,1600); //defaults to max_key_size=256,max_value_size=4096
assert(props);
//1-finally free all memory
xyz_config_free(&props);
And here the code snippet from the function where it sets to NULL
the local pointer passed tp the function by reference
if (NULL!=*pp_self)
{
free (*pp_self); //ERROR HAPPENS ON 2ND TIME HERE.
*pp_self = NULL;
}
That is *pp_self is not the same object as the object in the file scope p_self
.
So when the function xyz_config_free
is called the second time it tries to free already freed memory because before the second time the new memory was not allocated due to the fact that the file scope variable was not set to NULL in the previous call of the function xyz_config_free
The lessons from this bug is try to avoid to define functions that depend on file scope variables.