为什么我的C malloc断言失败了?

Chr*_*ris 71 c malloc gcc assertion

我正在实现一个除法和征服多项式算法,所以我可以将它与OpenCL实现进行基准测试,但我无法开始malloc工作.当我运行程序时,它会分配一堆东西,检查一些东西,然后发送size/2给算法.然后,当我malloc再次点击它时,它会吐出这个:

malloc.c:3096:sYSMALLOc:断言`(old_top ==(((mbinptr)(((char*)&((av) - > bins [((1) - 1)*2])) - __builtin_offsetof(struct malloc_chunk,fd))))&& old_size == 0)|| ((unsigned long)(old_size)> =(unsigned long)(((__ builtin_offsetof(struct malloc_chunk,fd_nextsize))+((2*(sizeof(size_t))) - 1))~~((2*(sizeof) (size_t))) - 1)))&&((old_top) - > size&0x1)&&((unsigned long)old_end&pagemask)== 0)'失败.中止

有问题的一行是:

int *mult(int size, int *a, int *b) {
    int *out,i, j, *tmp1, *tmp2, *tmp3, *tmpa1, *tmpa2, *tmpb1, *tmpb2,d, *res1, *res2;
    fprintf(stdout, "size: %d\n", size);

    out = (int *)malloc(sizeof(int) * size * 2);
}
Run Code Online (Sandbox Code Playgroud)

我用a检查了大小fprintf,它是一个正整数(此时通常为50).我尝试malloc用普通号码打电话,但我仍然得到错误.我只是对正在发生的事情感到难过,到目前为止我发现的谷歌没有任何帮助.

有什么想法发生了什么?我正在试图弄清楚如何编译一个更新的GCC,如果它是编译器错误,但我真的怀疑它.

R S*_*hko 84

99.9%的可能是你有内存损坏(缓冲区流量过大或流量不足,在释放后写入指针,在同一指针上调用两次,等等)

Valgrind下运行您的代码,看看您的程序在哪里做错了.

  • 固定的。Valgrind 绝对有帮助。我错误地转录了我的旧 matlab 代码,并有一个 for 循环迭代 j,然后在它内部执行 j++ ,它大部分覆盖了它正在写入的数组,并以某种方式导致 malloc 失败。谢谢您的帮助! (3认同)

Jon*_*set 65

为了让您更好地理解为什么会发生这种情况,我想对@ r-samuel-klatchko的答案进行一些扩展.

当你打电话时malloc,真正发生的事情比仅仅给你一大块内存要复杂一点.在引擎盖下,malloc还会保留一些关于它给你的内存(最重要的是它的大小)的内务处理信息,这样当你打电话时free,就会知道要释放多少内存.此信息通常保留在内存位置返回给您之前malloc.更多详尽的信息可以在互联网上找到,但(非常)基本的想法是这样的:

+------+-------------------------------------------------+
+ size |                  malloc'd memory                +
+------+-------------------------------------------------+
       ^-- location in pointer returned by malloc
Run Code Online (Sandbox Code Playgroud)

在此基础上(并大大简化了事情),当您调用时malloc,它需要获得指向可用内存的下一部分的指针.一种非常简单的方法是查看它给出的前一位内存,并size在内存中进一步向下(或向上)移动字节.通过这种实现,您在分配后最终会看到类似的内存p1,p2并且p3:

+------+----------------+------+--------------------+------+----------+
+ size |                | size |                    | size |          +
+------+----------------+------+--------------------+------+----------+
       ^- p1                   ^- p2                       ^- p3
Run Code Online (Sandbox Code Playgroud)

那么,是什么导致了您的错误?

好吧,想象一下你的代码错误地写了你已经分配的内存量(因为你分配的内容少于你需要的问题,或者因为你在代码中的某个地方使用了错误的边界条件).假设你的代码这么多的数据写入到p2它启动覆盖是什么p3size领域.当你现在下一次调用时malloc,它将查看它返回的最后一个内存位置,查看其大小字段,移动到p3 + size然后从那里开始分配内存.size但是,由于您的代码已被覆盖,因此该内存位置不再位于先前分配的内存之后.

毋庸置疑,这可能会造成严重破坏!因此,实现者malloc已经提出了许多"断言"或检查,如果它们即将发生,它们会尝试进行一系列的理智检查来捕获这个(以及其他问题).在您的特定情况下,这些断言被违反,因此malloc中止,告诉您您的代码即将执行它不应该做的事情.

如前所述,这是一个粗略的过度简化,但足以说明这一点.glibc的实现malloc是超过5k行,并且已经有大量关于如何构建良好的动态内存分配机制的研究,因此无法在SO答案中覆盖它.希望这能让您对实际导致问题的原因有所了解!

  • 这真的很有帮助! (2认同)

iBu*_*Bug 7

我使用Valgrind的替代解决方案:

我很高兴,因为我只是帮助我的朋友调试了程序。他的程序遇到了这个确切的问题(malloc()导致中止),并带有来自GDB的相同错误消息。

我编译使用他的程序地址消毒剂

gcc -Wall -g3 -fsanitize=address -o new new.c
              ^^^^^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

然后跑了gdb new。当程序SIGABRT由于后续原因而终止时malloc(),将打印很多有用的信息:

=================================================================
==407==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6060000000b4 at pc 0x7ffffe49ed1a bp 0x7ffffffedc20 sp 0x7ffffffed3c8
WRITE of size 104 at 0x6060000000b4 thread T0
    #0 0x7ffffe49ed19  (/usr/lib/x86_64-linux-gnu/libasan.so.4+0x5ed19)
    #1 0x8001dab in CreatHT2 /home/wsl/Desktop/hash/new.c:59
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
    #4 0x8001679 in _start (/mnt/d/Desktop/hash/new+0x1679)

0x6060000000b4 is located 0 bytes to the right of 52-byte region [0x606000000080,0x6060000000b4)
allocated by thread T0 here:
    #0 0x7ffffe51eb50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
    #1 0x8001d56 in CreatHT2 /home/wsl/Desktop/hash/new.c:55
    #2 0x80031cf in main /home/wsl/Desktop/hash/new.c:209
    #3 0x7ffffe061b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
Run Code Online (Sandbox Code Playgroud)

让我们看一下输出,尤其是堆栈跟踪:

第一部分说的写操作无效new.c:59。那行读

memset(len,0,sizeof(int*)*p);
             ^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

第二部分说发生错误写入的内存在处创建new.c:55。那行读

if(!(len=(int*)malloc(sizeof(int)*p))){
                      ^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

而已。我只花了不到半分钟的时间就找到了困扰我朋友几个小时的错误。他设法找到了故障,但是这是随后的malloc()调用失败,而无法在以前的代码中发现此错误。

总结:尝试使用-fsanitize=addressGCC或Clang。在调试内存问题时,它可能非常有用。