【问题标题】:What does 'corrupted double-linked list' mean“损坏的双链表”是什么意思
【发布时间】:2013-01-31 13:45:09
【问题描述】:

我最近从我的 PHP 中收到以下错误:

WARNING: [pool www] child 42475 said into stderr: "*** glibc detected *** php-fpm: pool www: corrupted double-linked list: 0x00000000013fe680 ***"

我对这个问题不是很在意,也对修复它不是很感兴趣。 但我很想了解这个错误“损坏的双链表”实际上意味着什么,因为我以前没有见过它。我相信知道什么是双链表,但是我没有生成一个触发这个错误的程序。

谁能给我一个简短的 sn-p 代码,当我编译和执行它时,它会导致 glibc 说“损坏的双链表”?

【问题讨论】:

  • 这可能是由于各种原因,我建议看这篇文章的人检查下面的所有答案以找到他们的案例

标签: memory-management glibc


【解决方案1】:

我自己找到了我的问题的答案:)

所以我不明白 glibc 如何区分 Segfault 和损坏的双链表,因为根据我的理解,从 glibc 的角度来看,它们看起来应该是一样的。 因为如果我在我的程序中实现一个双链表,glibc 怎么可能知道这是一个双链表,而不是任何其他结构?它可能不能,所以这就是我感到困惑的原因。

现在我查看了 glibc 代码中的 malloc/malloc.c,我看到了以下内容:

1543 /* Take a chunk off a bin list */
1544 #define unlink(P, BK, FD) {                                            \
1545   FD = P->fd;                                                          \
1546   BK = P->bk;                                                          \
1547   if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \
1548     malloc_printerr (check_action, "corrupted double-linked list", P); \
1549   else {                                                               \
1550     FD->bk = BK;                                                       \
1551     BK->fd = FD;                                                       \

所以现在这突然变得有意义了。 glibc 之所以能知道这是一个双链表,是因为该链表是 glibc 本身的一部分。我一直很困惑,因为我认为 glibc 可以以某种方式检测到某些编程正在构建一个双链表,我不明白它是如何工作的。但是如果它说的这个双链表是glibc本身的一部分,它当然可以知道它是一个双链表。

我仍然不知道是什么触发了这个错误。但至少我了解损坏的双链表和 Segfault 之间的区别,以及 glibc 如何知道该结构应该是双链表:)

【讨论】:

  • 如果 glibs 这么聪明会很有趣 ;) “糟糕的程序设计” --> crash
【解决方案2】:

对于在这里寻找解决方案的任何人,我在 C++ 中遇到了类似的问题: malloc(): smallbin 双链表损坏:

这是由于函数没有返回应有的值。

std::vector<Object> generateStuff(std::vector<Object>& target> {
  std::vector<Object> returnValue;
  editStuff(target);
  // RETURN MISSING
}

不知道为什么这毕竟能够编译。可能有关于它的警告。

【讨论】:

  • 我无法计算我为这种愚蠢的设计所浪费的时间。它不是错误的原因是因为你可能有一个你知道总是在某处调用return 的函数,但编译器不够聪明,无法推理。在这种情况下,在函数末尾有一个“额外的”return 是不必要的,并且会浪费内存和代码缓存。所以编译器就像“嘿,我只希望他们做对了”。然后您的程序在完全不相关的代码部分中由于无法理解的原因随机崩溃。
  • 顺便说一句,我强烈建议您添加-Werror=return-type 使其成为错误。您可能没有看到警告的原因是因为我猜generateStuff() 与崩溃的地方位于不同的翻译单元(.cpp 文件)中,如果您进行增量编译,该文件将永远不会被重新编译当你浪费时间在坠机现场摆弄时。
  • 实际上编译器在堆上分配期望返回的变量。这个地方在调用时就准备好了,但是这个内存充满了脏位,就像声明变量但未初始化时一样。一旦您尝试使用这些脏位做某事,它很可能会导致对象崩溃,但对于简单类型,返回值或多或少是随机的(例如 int :即使使用脏位,int 值也是可解释的)。在调试模式下,一些编译器将每一位内存都设置为零,因此您可能会有不同的行为。我完全同意@Timmmm,这应该是一个错误。
【解决方案3】:

我在一些代码中遇到了这个错误,其中有人在一个线程中调用 exit() 与 main() 返回的时间差不多,因此所有全局/静态构造函数同时在两个单独的线程中启动。

此错误还表现为double free or corruption,或exit()malloc_consolidate 内部的段错误/sig11,以及可能的其他错误。 malloc_consolidate 崩溃的调用堆栈可能类似于:

#0  0xabcdabcd in malloc_consolidate () from /lib/libc.so.6
#1  0xabcdabcd in _int_free () from /lib/libc.so.6
#2  0xabcdabcd in operator delete (...)
#3  0xabcdabcd in operator delete[] (...)
(...)

在 valgrind 下运行时,我无法让它出现这个问题。

【讨论】:

    【解决方案4】:

    堆溢出应该归咎于(但不总是)corrupted double-linked listmalloc(): memory corruptiondouble free or corruption (!prev)-like glibc 警告。

    应通过以下代码重现:

    #include <vector>
    
    using std::vector;
    
    
    int main(int argc, const char *argv[])
    {
        int *p = new int[3];
        vector<int> vec;
        vec.resize(100);
        p[6] = 1024;
        delete[] p;
        return 0;
    }
    

    如果使用 g++ (4.5.4) 编译:

    $ ./heapoverflow
    *** glibc detected *** ./heapoverflow: double free or corruption (!prev): 0x0000000001263030 ***
    ======= Backtrace: =========
    /lib64/libc.so.6(+0x7af26)[0x7f853f5d3f26]
    ./heapoverflow[0x40138e]
    ./heapoverflow[0x400d9c]
    ./heapoverflow[0x400bd9]
    ./heapoverflow[0x400aa6]
    ./heapoverflow[0x400a26]
    /lib64/libc.so.6(__libc_start_main+0xfd)[0x7f853f57b4bd]
    ./heapoverflow[0x4008f9]
    ======= Memory map: ========
    00400000-00403000 r-xp 00000000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
    00602000-00603000 r--p 00002000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
    00603000-00604000 rw-p 00003000 08:02 2150398851                         /data1/home/mckelvin/heapoverflow
    01263000-01284000 rw-p 00000000 00:00 0                                  [heap]
    7f853f559000-7f853f6fa000 r-xp 00000000 09:01 201329536                  /lib64/libc-2.15.so
    7f853f6fa000-7f853f8fa000 ---p 001a1000 09:01 201329536                  /lib64/libc-2.15.so
    7f853f8fa000-7f853f8fe000 r--p 001a1000 09:01 201329536                  /lib64/libc-2.15.so
    7f853f8fe000-7f853f900000 rw-p 001a5000 09:01 201329536                  /lib64/libc-2.15.so
    7f853f900000-7f853f904000 rw-p 00000000 00:00 0
    7f853f904000-7f853f919000 r-xp 00000000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
    7f853f919000-7f853fb19000 ---p 00015000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
    7f853fb19000-7f853fb1a000 r--p 00015000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
    7f853fb1a000-7f853fb1b000 rw-p 00016000 09:01 74726670                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libgcc_s.so.1
    7f853fb1b000-7f853fc11000 r-xp 00000000 09:01 201329538                  /lib64/libm-2.15.so
    7f853fc11000-7f853fe10000 ---p 000f6000 09:01 201329538                  /lib64/libm-2.15.so
    7f853fe10000-7f853fe11000 r--p 000f5000 09:01 201329538                  /lib64/libm-2.15.so
    7f853fe11000-7f853fe12000 rw-p 000f6000 09:01 201329538                  /lib64/libm-2.15.so
    7f853fe12000-7f853fefc000 r-xp 00000000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
    7f853fefc000-7f85400fb000 ---p 000ea000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
    7f85400fb000-7f8540103000 r--p 000e9000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
    7f8540103000-7f8540105000 rw-p 000f1000 09:01 74726678                   /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.1/libstdc++.so.6.0.18
    7f8540105000-7f854011a000 rw-p 00000000 00:00 0
    7f854011a000-7f854013c000 r-xp 00000000 09:01 201328977                  /lib64/ld-2.15.so
    7f854031c000-7f8540321000 rw-p 00000000 00:00 0
    7f8540339000-7f854033b000 rw-p 00000000 00:00 0
    7f854033b000-7f854033c000 r--p 00021000 09:01 201328977                  /lib64/ld-2.15.so
    7f854033c000-7f854033d000 rw-p 00022000 09:01 201328977                  /lib64/ld-2.15.so
    7f854033d000-7f854033e000 rw-p 00000000 00:00 0
    7fff92922000-7fff92943000 rw-p 00000000 00:00 0                          [stack]
    7fff929ff000-7fff92a00000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    [1]    18379 abort      ./heapoverflow
    

    如果使用 clang++(6.0 (clang-600.0.56)) 编译:

    $  ./heapoverflow
    [1]    96277 segmentation fault  ./heapoverflow
    

    如果您认为自己可能编写了这样的错误,这里有一些提示可以找出它。

    首先,编译带有调试标志(-g)的代码:

    g++ -g foo.cpp
    

    然后,使用valgrind 运行它:

    $ valgrind ./a.out
    ==12693== Memcheck, a memory error detector
    ==12693== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
    ==12693== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
    ==12693== Command: ./a.out
    ==12693==
    ==12693== Invalid write of size 4
    ==12693==    at 0x400A25: main (foo.cpp:11)
    ==12693==  Address 0x5a1c058 is 12 bytes after a block of size 12 alloc'd
    ==12693==    at 0x4C2B800: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
    ==12693==    by 0x4009F6: main (foo.cpp:8)
    ==12693==
    ==12693==
    ==12693== HEAP SUMMARY:
    ==12693==     in use at exit: 0 bytes in 0 blocks
    ==12693==   total heap usage: 2 allocs, 2 frees, 412 bytes allocated
    ==12693==
    ==12693== All heap blocks were freed -- no leaks are possible
    ==12693==
    ==12693== For counts of detected and suppressed errors, rerun with: -v
    ==12693== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
    

    错误位于 ==12693== at 0x400A25: main (foo.cpp:11)

    【讨论】:

    • 示例代码中的向量声明与错误有什么关系?
    • @SamuelLi 我无​​法用这段代码和 g++ 9.3.0 重现错误,但想法应该是这样的:在分配内存时,会分配一些额外的内存来管理内存存储,例如指向下一个分配内存块(链表)的指针。额外的向量可用于生成第二个malloced 块,并且通过写入 p[6],包含向量内部缓冲区的块的地址可能会被破坏。由于我无法重现此错误,存储格式可能在 g++4.5.4 和 g++ 9.3.0 之间发生了变化
    • @schetefan24 我明白你的意思了,谢谢你的解释!
    【解决方案5】:

    这可能是由于各种原因,人们提到了其他可能性,我补充一下我的情况:

    我在使用多线程(std::pthreadstd::thread)时遇到了这个错误,因为我忘记锁定一个变量,多个线程可能同时更改。 这是一个运行时错误,在某些运行中是随机出现的,但不是全部,因为...您知道两个线程之间的意外是随机的。

    在我的例子中,那个变量是一个全局的std::vector,我试图在线程调用的函数中将push_back() 的东西插入到一个函数中。然后我使用了std::mutex,再也没有出现这个错误。

    可能会有所帮助

    【讨论】:

      【解决方案6】:

      一位同事收到此错误,并发现他在代码中某处对列表元素犯了此错误:

       std::string listElement = listElement = someObject.getName();
      

      显然不是:

       std::string listElement = someObject.getName();
      

      这似乎无关,但每次运行时都会出现错误,我们可以在清理所有内容后重现它,仅更改这一行就解决了问题。

      希望有一天它对某人有所帮助....

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-01-15
        • 2015-09-22
        • 1970-01-01
        • 1970-01-01
        • 2014-03-23
        • 1970-01-01
        • 1970-01-01
        • 2016-01-01
        相关资源
        最近更新 更多