9

我的程序中有一个奇怪的错误,在我看来 malloc() 导致了 SIGSEGV,据我所知,这没有任何意义。我正在为动态列表使用一个名为 simclist 的库。

这是一个稍后引用的结构:

typedef struct {
    int msgid;
    int status;
    void* udata;
    list_t queue;
} msg_t;

这是代码:

msg_t* msg = (msg_t*) malloc( sizeof( msg_t ) );

msg->msgid = msgid;
msg->status = MSG_STAT_NEW;
msg->udata = udata;
list_init( &msg->queue );

list_init是程序失败的地方,这里是 list_init 的代码:

/* list initialization */
int list_init(list_t *restrict l) {
    if (l == NULL) return -1;

    srandom((unsigned long)time(NULL));

    l->numels = 0;

    /* head/tail sentinels and mid pointer */
    l->head_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->tail_sentinel = (struct list_entry_s *)malloc(sizeof(struct list_entry_s));
    l->head_sentinel->next = l->tail_sentinel;
    l->tail_sentinel->prev = l->head_sentinel;
    l->head_sentinel->prev = l->tail_sentinel->next = l->mid = NULL;
    l->head_sentinel->data = l->tail_sentinel->data = NULL;

    /* iteration attributes */
    l->iter_active = 0;
    l->iter_pos = 0;
    l->iter_curentry = NULL;

    /* free-list attributes */
    l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS * sizeof(struct list_entry_s *));
    l->spareelsnum = 0;

#ifdef SIMCLIST_WITH_THREADS
    l->threadcount = 0;
#endif

    list_attributes_setdefaults(l);

    assert(list_repOk(l));
    assert(list_attrOk(l));

    return 0;
}

该行l->spareels = (struct list_entry_s **)malloc(SIMCLIST_MAX_SPARE_ELEMS *是根据堆栈跟踪导致 SIGSEGV 的位置。我正在使用 gdb/nemiver 进行调试,但不知所措。第一次调用此函数时它工作正常,但第二次总是失败。malloc() 如何导致 SIGSEGV?

这是堆栈跟踪:

#0  ?? () at :0
#1  malloc () at :0
#2  list_init (l=0x104f290) at src/simclist.c:205
#3  msg_new (msg_switch=0x1050dc0, msgid=8, udata=0x0) at src/msg_switch.c:218
#4  exread (sockfd=8, conn_info=0x104e0e0) at src/zimr-proxy/main.c:504
#5  zfd_select (tv_sec=0) at src/zfildes.c:124
#6  main (argc=3, argv=0x7fffcabe44f8) at src/zimr-proxy/main.c:210

非常感谢任何帮助或见解!

4

6 回答 6

27

malloc例如,当堆损坏时,可能会出现段错误。检查您没有写任何超出任何先前分配范围的内容。

于 2009-09-17T20:07:06.610 回答
17

您的代码的其他部分可能会发生内存冲突。如果你在 Linux 上,你绝对应该尝试 valgrind。我永远不会相信我自己的 C 程序,除非它通过 valgrind。

编辑:另一个有用的工具是电子围栏。Glibc 还提供了MALLOC_CHECK_环境变量来帮助调试内存问题。这两种方法对运行速度的影响不如 valgrind。

于 2009-09-17T20:10:32.857 回答
12

free在此调用之前,您可能已经通过缓冲区溢出或使用未分配malloc(或已释放)的指针调用破坏了堆。

如果 malloc 使用的内部数据结构以这种方式损坏,则 malloc 正在使用无效数据并可能崩溃。

于 2009-09-17T20:07:34.953 回答
4

malloc()从(and realloc()and )触发核心转储的方法有很多种calloc()。这些包括:

  • 缓冲区溢出:写入超出分配空间的末尾(践踏malloc()保留在那里的控制信息)。
  • 缓冲区下溢:在分配空间开始之前写入(践踏malloc()保留在那里的控制信息)。
  • 释放未被分配的内存malloc()。在混合 C 和 C++ 程序中,这将包括释放 C++ 中由new.
  • 释放指向分配的内存块的一部分的指针malloc()- 这是前一种情况的特殊情况。
  • 释放一个已经被释放的指针——臭名昭著的“双重释放”。

使用malloc()系统标准版本的诊断版本或启用诊断可能有助于识别其中一些问题。例如,它可能能够检测到小的下溢和溢出(因为它会分配额外的空间以在您请求的空间周围提供缓冲区),并且它可能会检测到释放未分配或已释放内存的尝试或指针部分通过分配的空间 - 因为它将与分配的空间分开存储信息。代价是调试版本占用更多空间。一个非常好的分配器将能够记录堆栈跟踪和行号,以告诉您代码中分配发生的位置,或者第一次空闲发生的位置。

于 2009-09-17T20:19:25.400 回答
2

您应该尝试单独调试此代码,以查看问题是否实际位于生成段错误的位置。(我怀疑不是)。

这表示:

#1:使用-O0 编译代码,以确保gdb 获得正确的行号信息。

#2:编写一个调用这部分代码的单元测试。

我的猜测是代码在单独使用时会正常工作。然后,您可以以相同的方式测试您的其他模块,直到找出导致错误的原因。

正如其他人所建议的,使用 Valgrind 也是一个非常好的主意。

于 2009-09-17T20:17:53.280 回答
0

代码有问题。如果 malloc 返回 NULL,则您的代码中没有正确处理这种情况。您只是假设实际上没有为您分配内存。这可能会导致内存损坏。

于 2009-09-27T09:12:29.377 回答