【问题标题】:How can malloc() cause a SIGSEGV?malloc() 如何导致 SIGSEGV?
【发布时间】:2010-11-29 06:53:15
【问题描述】:

我的程序中有一个奇怪的错误,在我看来 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

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

【问题讨论】:

  • 附带说明,不建议多次致电srandom()。为了避免将来出现错误,即使在 list_init() 中已知仅被调用一次,您应该将该播种移到更明显执行一次的位置,例如靠近 main() 的顶部。

标签: c gdb malloc segmentation-fault


【解决方案1】:

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

【讨论】:

  • 并使用 valgrind!其他海报提到了这个工具,但遗憾的是没有得到很多支持。这个答案没有错,但是如果没有使用 valgrind 的警告,任何关于内存损坏的讨论都是不完整的!
  • 我在调用 free() 时得到了一个 sigsegv。我在 valgrind 下运行代码,它表明我已经超出了缓冲区并损坏了堆。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

      这意味着:

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

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

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

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

      【讨论】:

        【解决方案5】:

        有多种方法可以从malloc()(以及realloc()calloc())触发核心转储。其中包括:

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

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-11-09
            • 1970-01-01
            • 2010-12-06
            • 1970-01-01
            • 1970-01-01
            • 2016-06-06
            • 2014-10-22
            • 1970-01-01
            相关资源
            最近更新 更多