Home > Back-end >  C double free error after 2nd run of the code
C double free error after 2nd run of the code

Time:12-04

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:

enter image description here

The debugger memory & variables watch at the 2nd run where there is a problem:

enter image description here

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.

  • Related