2009-09-17 575 views
8

我的程序中有一个奇怪的错误,在我看来,malloc()导致了一个SIGSEGV,就我的理解而言,这没有任何意义。我正在使用一个名为simclist的库用于动态列表。malloc()如何导致SIGSEGV?

这是以后引用一个结构:

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是根据t o堆栈跟踪。我正在使用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 

任何帮助或洞察力非常感谢!

+0

如何声明'list_t'? – 2009-09-17 20:05:37

+0

作为一个方面说明,不建议多次调用'srandom()'。为了避免将来的错误,即使在'list_init()中被称为只被调用一次,你应该将该种子移动到更明显被执行一次的地方,比如'main()'的顶部附近。 – RBerteig 2009-09-17 21:26:40

回答

25

malloc可以segfault,例如当堆损坏。检查你是不是写任何超出任何以前的分配范围。

+20

并使用valgrind!其他海报提到这个工具,但遗憾的是没有得到许多upvotes。这个答案没有错,但没有任何劝告使用valgrind的任何内存损坏的讨论是不完整的! – 2009-09-17 21:59:40

+2

我在调用free()时得到一个sigsegv。我在valgrind下运行了代码,结果表明我写了超出缓冲区并损坏了堆。 – 2011-05-21 04:30:41

12

你可能已经损坏,你这个电话由缓冲区溢出或致电free与没有被分配malloc(或已被释放)的指针在什么地方堆。

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

16

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

编辑:另一个有用的工具是Electric fence。 Glibc还提供MALLOC_CHECK_环境变量来帮助调试内存问题。这两种方法不会像valgrind那样影响跑步速度。

1

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

这意味着:

#1:编译代码-O0,以确保GDB得到正确的行号信息。

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

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

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

4

有从malloc()(和realloc()calloc())触发核心转储的种种方法。这些措施包括:

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

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

0

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