第 2 次运行代码后出现 C 双重释放错误

Posted

技术标签:

【中文标题】第 2 次运行代码后出现 C 双重释放错误【英文标题】:C double free error after 2nd run of the code 【发布时间】:2022-01-10 04:54:53 【问题描述】:

我正在尝试用 C 语言开发一个库,并从内存管理开始。

尝试多次测试我的分配函数和释放函数以测试多个分配和释放。

但是,在免费功能的第二次运行中,我遇到了双重免费和崩溃。

我的简单标题:

#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);

实现 C 文件:

#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; 
        

    

主文件:

/*
 * 
 */
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");

调试器内存和变量在第一次运行时观察:

调试器内存和变量在第 2 次运行出现问题时观察:

任何帮助将不胜感激。

【问题讨论】:

为什么你有全局的p_self 变量?我认为如果你摆脱它,调试会容易得多。 @EdmCoff,这只是变量所在的位置,它只是指向包含其他数据结构和其他相关数据的结构的指针。如果没有 OOP,就很难做到,不管它与双重释放没有直接关系。你会建议什么模式? @EdmCoff,好的,我删除了 extern 关键字。它是早期版本的遗物。 【参考方案1】:

似乎问题与在文件范围内声明变量 p_self 有关

xyz_config_list* p_self = NULL;

此变量用于函数xyz_config_alloc_helper。仅当文件范围变量p_self 等于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);
                                                               ^^^^^^^

和(在函数xyz_config_alloc_helper内)

 if (NULL == *props) 
    *props = (xyz_config_list*) calloc(1, sizeof(xyz_config_list));

但是,在函数xyz_config_free 中,变量未设置为NULL,因为该函数处理在函数 test 和 test2 中声明的局部变量,例如

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);

这里的代码 sn-p 从它设置为NULL 的函数中通过引用传递给函数

    if (NULL!=*pp_self)
    
        free (*pp_self); //ERROR HAPPENS ON 2ND TIME HERE.
        *pp_self = NULL; 
    

即*pp_self与文件范围p_self内的对象不是同一个对象。

因此,当第二次调用函数xyz_config_free 时,它会尝试释放已释放的内存,因为在第二次之前,由于文件范围变量未设置为 NULL,因此未分配新内存函数xyz_config_free的调用

这个错误的教训是尽量避免定义依赖于文件范围变量的函数。

【讨论】:

经验教训。但是,如果我不全局声明它,我将如何避免在每次调用时传递 p_self ??? @cyber101 是什么阻止了通过引用函数 xyz_config_alloc 来传递本地指针?否则,您需要在释放分配的内存的函数中将文件范围变量设置为 NULL 是的,但是公开需要引用该结构的函数对于最终用户来说看起来很难看。我只是想要这样的东西 add(key,value) 而不是 add(key,value,&pp_self) 在标题中。 @cyber101 不幸的是我什么也没说,因为我不知道程序的设计思想。我已经指出了运行时错误的原因。

以上是关于第 2 次运行代码后出现 C 双重释放错误的主要内容,如果未能解决你的问题,请参考以下文章

在第6731次释放指针后双重免费或损坏

PHP 退出并出现“双重释放或损坏”错误

为啥使用 realloc() 时会出现双重释放或损坏错误?

MySQL 重启使用 MySQL C API 导致双重释放或损坏

C++ 双重释放或损坏(出)

assertion failed是啥原因?