【问题标题】:free() invalid pointer - freeing array of pointers failsfree() 无效指针 - 释放指针数组失败
【发布时间】:2012-03-16 15:22:11
【问题描述】:

我一直在调试一段遗留代码,在带有 linux 的 XScale (arm v5te) 系统上运行,崩溃可重现。

我已经使用 gdb 进行了调试,并将 MALLOC_CHECK_ 设置为 1。代码很多,所以只需一些 sn-ps:

我们有这样的结构:

typedef struct {
...clipped..
    char **data_column_list;
    /** data column count */
    int data_column_cnt;
...clipped
} csv_t;

我们在一个函数中初始化列,把它们放在一个变量“columns”中

/* Allocating memory for pointer to every register id */
columns = (char **) malloc(column_cnt * sizeof(char *));

column_cnt = 0;
/* loop over all sensors */
for(i=0; i<cfg.sen_cnt; i++) {
    /* loop over all registers */
    for(j=0; j<cfg.sen_list[i]->data_cnt; j++) {
        /* Storing all the pointers to id */
        columns[column_cnt++] = cfg.sen_list[i]->data_list[j]->id;
    }
}

在另一个函数中,会发生这样的事情:

/* free the previous list */
csv_free(lc_csv);

lc_csv->data_column_list = columns;
lc_csv->data_column_cnt = column_cnt;

csv_free 存在:

void csv_free(csv_t *csv) {
    if(csv->data_column_cnt > 0)
        free(csv->data_column_list);

    csv->data_column_cnt = 0;
}

现在,还有另一个函数,构建了包含这些 id 的整个“cfg”/config 结构。 上面的代码:cfg.sen_list[i]->data_list[j]->id;其中 cfg 是一个结构,sen_list 是一个指向结构的指针数组,data_list 是一个指向其他结构的指针数组,其中包含一个字符串“id”。

当程序收到信号 SIGUSR1 时,正在更新配置。所有这些 data_list 和 sen_list 结构都被释放,然后生成新的结构。 然后使用第一个函数,生成新的 id 列并将其放入 csv 结构中,但之前已释放旧列表。

那就是它崩溃的地方。在 csv_free 中。

*** glibc detected *** /root/elv: free(): invalid pointer: 0x0001ae88 ***

我认为应该是这样的。你有一个指针数组。当你释放指针时,你必须释放指针,指向一组指针(数组)。 或者换个代码来说,上面的情况应该类比为:

char **ar = malloc(n * sizeof(char *));
char *xn = malloc(10 * sizeof(char)); // Do for 0 to n strings
...
ar[n] = xn; // Do for 0 to n strings
...do stuff...
free(xn); // Do for 0 to n strings
free(ar);

当包含 id 字符串的结构被释放时,我的指针数组仍然带有(无效)指针,而不是空指针:

(gdb) p csv
$40 = {sysid = 222, ip = '\0' <repeats 49 times>, 
    module = "elv_v2", '\0' <repeats 14 times>, format_type = 1, msg_id = 0, 
    data_column_list = 0x1ae88, data_column_cnt = 10, pub_int = 30, 
    line_cnt = 0, pub_seq = -1, format = 0x18260}
(gdb) p csv.data_column_list[0]
$41 = 0x1b378 "0"

但我收到上述错误消息(或没有 MALLOC_CHECK_ 的 SIGABRT)。 我完全不明白这一点。我必须释放这个指针数组,否则它将成为内存泄漏。在此之前,我找不到其他免费的电话。我不知道为什么 csv.data_column_list 被认为是无效指针。 不幸的是,Valgrind 在 arm v5te 上不可用 :(

已经调试了好几个小时,很乐意提供任何帮助。 非常感谢你, 干杯, 本

更新:

我想知道它是否与某些“范围”问题有关。另一个应用程序中有几乎相同的代码,它可以工作。两个程序都使用崩溃的函数“csv_free”(静态链接)。唯一的区别是,包含要释放的指针的结构在工作程序中正常声明和定义,并声明为external,并在除 main.c 之外的另一个文件中定义 在 main.c 中手动调用“free”有效,而调用“csv_free”则崩溃。猜我这个...

【问题讨论】:

  • 你试过用 valgrind 运行它吗?
  • 如果你在 malloc 之后和 free 之前断点,所有的指针值都匹配吗?
  • 您应该检查csv 是否在csv_free 中,并在释放csv 后将其分配为NULL。当 csv 为 NULL 或一些垃圾时,这将保护您免于执行 csv-&gt;member
  • 你说你在收到 SIGUSR1 后释放指针。那是在信号处理程序内部吗? SIGUSR1 可以随时交付,因此在信号处理程序中 malloc 或 free 可能是不安全的。如果在程序在另一个 malloc/free 调用中传递信号,则可能会损坏堆。
  • 感谢大家的阅读和评论! @Als - 正如我所说,Valgrind 不适用于该平台。我正在考虑在我的 x86-64 机器上运行它,但我需要重新创建环境以使软件工作,例如,制作虚拟串行端口并向它们提供信息。但这是相当大的努力。

标签: c memory-leaks malloc free


【解决方案1】:

当我遇到 free() 错误时,有 10 次中有 9 次问题实际上是在分配或初始化时开始的,所以让我们来验证一下:

  1. 在调用csv_free之前,您实际上在哪里将columns 分配给csv.data_columns_list?如果 free() 时未初始化,则可以解释错误。

  2. 在第二个代码块中,如果初始 column_cnt(我猜是设置 其他地方?)小于您将在循环之外编写的 column_cnt 大批。人们希望 MALLOC_CHECK_ 能抓住这一点,但如果你断言为 如下:

    /* Allocating memory for pointer to every register id */
    columns = (char **) malloc(column_cnt * sizeof(char *));
    
    int old_column_cnt = column_cnt;
    column_cnt = 0;
    /* loop over all sensors */
    for(i=0; i<cfg.sen_cnt; i++) {
        /* loop over all registers */
        for(j=0; j<cfg.sen_list[i]->data_cnt; j++) {
            /* Storing all the pointers to id */
            columns[column_cnt++] = cfg.sen_list[i]->data_list[j]->id;
        }
    }
    assert(old_column_cnt >= column_cnt);
    

【讨论】:

    【解决方案2】:

    查看我的旧问题时,我看到了这一点。我无法真正验证,因为我不再在那家公司工作,但考虑到我们遇到的其他问题,我认为 wildplasser 是对的。

    从信号处理程序中调用任何大型函数都是一个坏主意。特别是如果你没有检查你所做的一切是否是可重入的。这是遗留代码,所以至少不完全是我的错;)

    现在我会在信号处理程序中设置一个标志,并在设置该标志(或类似的东西)时在我的主循环中调用例程。

    【讨论】:

      猜你喜欢
      • 2021-10-07
      • 2012-02-21
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 2013-06-27
      • 2015-03-31
      • 2017-04-02
      • 1970-01-01
      相关资源
      最近更新 更多