关于c ++:如何跟踪“双重释放或损坏”错误

How to track down a “double free or corruption” error

当我运行(C ++)程序时,它因该错误而崩溃。

* glibc detected * ./load: double free or corruption (!prev):
0x0000000000c6ed50 ***

如何跟踪错误?

我尝试使用print(std::cout)语句,但未成功。 gdb可以使这更容易吗?


如果您使用的是glibc,则可以将MALLOC_CHECK_环境变量设置为2,这将导致glibc使用容错版本的malloc,这将导致您的程序在两次释放的点中止。已经完成了。

您可以在运行程序之前使用set environment MALLOC_CHECK_ 2命令从gdb进行设置。程序应该中止,在回溯中可见free()调用。

有关更多信息,请参见malloc()的手册页。


至少有两种可能的情况:

  • 您要删除同一实体两次
  • 您正在删除未分配的内容
  • 对于第一个,我强烈建议对所有已删除的指针进行NULL处理。

    您有三种选择:

  • 重载new并删除并跟踪分配
  • 是的,请使用gdb -然后您将从崩溃中获取回溯信息,这可能会非常有帮助
  • 根据建议-使用Valgrind-进入并不容易,但是它将在未来节省数千倍的时间...

  • 您可以使用gdb,但我会先尝试Valgrind。请参阅快速入门指南。

    简要地说,Valgrind对您的程序进行了检测,以便它可以在使用动态分配的内存时检测几种错误,例如,两次释放并在分配的内存块末尾进行写操作(这可能会破坏堆)。它会立即检测并报告错误,从而使您直接找到问题的原因。


    三个基本规则:

  • 释放后将指针设置为NULL
  • 释放前检查NULL
  • 首先初始化指向NULL的指针。
  • 这三者的结合效果很好。


    您可以使用valgrind对其进行调试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    #include<stdio.h>
    #include<stdlib.h>

    int main()
    {
     char *x = malloc(100);
     free(x);
     free(x);
     return 0;
    }

    [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
    [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
    [sand@PS-CNTOS-64-S11 testbox]$ ./t1
    *** glibc detected *** ./t1: double free or corruption (top): 0x00000000058f7010 ***
    ======= Backtrace: =========
    /lib64/libc.so.6[0x3a3127245f]
    /lib64/libc.so.6(cfree+0x4b)[0x3a312728bb]
    ./t1[0x400500]
    /lib64/libc.so.6(__libc_start_main+0xf4)[0x3a3121d994]
    ./t1[0x400429]
    ======= Memory map: ========
    00400000-00401000 r-xp 00000000 68:02 30246184                           /home/sand/testbox/t1
    00600000-00601000 rw-p 00000000 68:02 30246184                           /home/sand/testbox/t1
    058f7000-05918000 rw-p 058f7000 00:00 0                                  [heap]
    3a30e00000-3a30e1c000 r-xp 00000000 68:03 5308733                        /lib64/ld-2.5.so
    3a3101b000-3a3101c000 r--p 0001b000 68:03 5308733                        /lib64/ld-2.5.so
    3a3101c000-3a3101d000 rw-p 0001c000 68:03 5308733                        /lib64/ld-2.5.so
    3a31200000-3a3134e000 r-xp 00000000 68:03 5310248                        /lib64/libc-2.5.so
    3a3134e000-3a3154e000 ---p 0014e000 68:03 5310248                        /lib64/libc-2.5.so
    3a3154e000-3a31552000 r--p 0014e000 68:03 5310248                        /lib64/libc-2.5.so
    3a31552000-3a31553000 rw-p 00152000 68:03 5310248                        /lib64/libc-2.5.so
    3a31553000-3a31558000 rw-p 3a31553000 00:00 0
    3a41c00000-3a41c0d000 r-xp 00000000 68:03 5310264                        /lib64/libgcc_s-4.1.2-20080825.so.1
    3a41c0d000-3a41e0d000 ---p 0000d000 68:03 5310264                        /lib64/libgcc_s-4.1.2-20080825.so.1
    3a41e0d000-3a41e0e000 rw-p 0000d000 68:03 5310264                        /lib64/libgcc_s-4.1.2-20080825.so.1
    2b1912300000-2b1912302000 rw-p 2b1912300000 00:00 0
    2b191231c000-2b191231d000 rw-p 2b191231c000 00:00 0
    7ffffe214000-7ffffe229000 rw-p 7ffffffe9000 00:00 0                      [stack]
    7ffffe2b0000-7ffffe2b4000 r-xp 7ffffe2b0000 00:00 0                      [vdso]
    ffffffffff600000-ffffffffffe00000 ---p 00000000 00:00 0                  [vsyscall]
    Aborted
    [sand@PS-CNTOS-64-S11 testbox]$


    [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
    [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
    [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck ./t1
    ==20859== Memcheck, a memory error detector
    ==20859== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    ==20859== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
    ==20859== Command: ./t1
    ==20859==
    ==20859== Invalid free() / delete / delete[]
    ==20859==    at 0x4A05A31: free (vg_replace_malloc.c:325)
    ==20859==    by 0x4004FF: main (t1.c:8)
    ==20859==  Address 0x4c26040 is 0 bytes inside a block of size 100 free'
    d
    ==20859==    at 0x4A05A31: free (vg_replace_malloc.c:325)
    ==20859==    by 0x4004F6: main (t1.c:7)
    ==20859==
    ==20859==
    ==20859== HEAP SUMMARY:
    ==20859==     in use at exit: 0 bytes in 0 blocks
    ==20859==   total heap usage: 1 allocs, 2 frees, 100 bytes allocated
    ==20859==
    ==20859== All heap blocks were freed -- no leaks are possible
    ==20859==
    ==20859== For counts of detected and suppressed errors, rerun with: -v
    ==20859== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
    [sand@PS-CNTOS-64-S11 testbox]$


    [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1
    ==20899== Memcheck, a memory error detector
    ==20899== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    ==20899== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
    ==20899== Command: ./t1
    ==20899==
    ==20899== Invalid free() / delete / delete[]
    ==20899==    at 0x4A05A31: free (vg_replace_malloc.c:325)
    ==20899==    by 0x4004FF: main (t1.c:8)
    ==20899==  Address 0x4c26040 is 0 bytes inside a block of size 100 free'
    d
    ==20899==    at 0x4A05A31: free (vg_replace_malloc.c:325)
    ==20899==    by 0x4004F6: main (t1.c:7)
    ==20899==
    ==20899==
    ==20899== HEAP SUMMARY:
    ==20899==     in use at exit: 0 bytes in 0 blocks
    ==20899==   total heap usage: 1 allocs, 2 frees, 100 bytes allocated
    ==20899==
    ==20899== All heap blocks were freed -- no leaks are possible
    ==20899==
    ==20899== For counts of detected and suppressed errors, rerun with: -v
    ==20899== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
    [sand@PS-CNTOS-64-S11 testbox]$

    一种可能的解决方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #include<stdio.h>
    #include<stdlib.h>

    int main()
    {
     char *x = malloc(100);
     free(x);
     x=NULL;
     free(x);
     return 0;
    }

    [sand@PS-CNTOS-64-S11 testbox]$ vim t1.c
    [sand@PS-CNTOS-64-S11 testbox]$ cc -g t1.c -o t1
    [sand@PS-CNTOS-64-S11 testbox]$ ./t1
    [sand@PS-CNTOS-64-S11 testbox]$

    [sand@PS-CNTOS-64-S11 testbox]$ valgrind --tool=memcheck --leak-check=full ./t1
    ==20958== Memcheck, a memory error detector
    ==20958== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
    ==20958== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
    ==20958== Command: ./t1
    ==20958==
    ==20958==
    ==20958== HEAP SUMMARY:
    ==20958==     in use at exit: 0 bytes in 0 blocks
    ==20958==   total heap usage: 1 allocs, 1 frees, 100 bytes allocated
    ==20958==
    ==20958== All heap blocks were freed -- no leaks are possible
    ==20958==
    ==20958== For counts of detected and suppressed errors, rerun with: -v
    ==20958== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
    [sand@PS-CNTOS-64-S11 testbox]$

    查看有关使用Valgrind Link的博客


    使用现代C ++编译器,您可以使用消毒剂进行跟踪。

    示例示例:

    我的程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $cat d_free.cxx
    #include<iostream>

    using namespace std;

    int main()

    {
       int * i = new int();
       delete i;
       //i = NULL;
       delete i;
    }

    用地址清理器进行编译:

    1
    # g++-7.1 d_free.cxx -Wall -Werror -fsanitize=address -g

    执行 :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # ./a.out
    =================================================================
    ==4836==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0:
        #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140
        #1 0x400b2c in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:11
        #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)
        #3 0x400a08  (/media/sf_shared/jkr/cpp/d_free/a.out+0x400a08)

    0x602000000010 is located 0 bytes inside of 4-byte region [0x602000000010,0x602000000014)
    freed by thread T0 here:
        #0 0x7f35b2d7b3c8 in operator delete(void*, unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140
        #1 0x400b1b in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:9
        #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)

    previously allocated by thread T0 here:
        #0 0x7f35b2d7a040 in operator new(unsigned long) /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:80
        #1 0x400ac9 in main /media/sf_shared/jkr/cpp/d_free/d_free.cxx:8
        #2 0x7f35b2050c04 in __libc_start_main (/lib64/libc.so.6+0x21c04)

    SUMMARY: AddressSanitizer: double-free /media/sf_shared/gcc-7.1.0/libsanitizer/asan/asan_new_delete.cc:140 in operator delete(void*, unsigned long)
    ==4836==ABORTING

    要了解有关消毒剂的更多信息,可以查看此文档或此文档或任何现代c ++编译器(例如gcc,clang等)文档。


    您是否正在使用诸如Boost shared_ptr之类的智能指针?如果是这样,请通过调用get()检查您是否在任何地方直接使用原始指针。我发现这是一个非常普遍的问题。

    例如,设想一个场景,其中将原始指针(例如,作为回调处理程序)传递给了代码。您可能决定将其分配给智能指针,以应对引用计数等问题。大错误:除非进行深拷贝,否则代码不拥有此指针。当您的代码使用智能指针完成后,它将销毁它并尝试销毁它指向的内存,因为它认为没有其他人需要它,但是调用代码随后将尝试删除它,并且您会得到两倍免费的问题。

    当然,这可能不是您的问题。最简单的是,下面的示例说明了它如何发生。第一次删除是可以的,但是编译器会感觉到它已经删除了该内存并导致了问题。因此,删除后立即为指针分配0是一个好主意。

    1
    2
    3
    4
    5
    6
    7
    8
    int main(int argc, char* argv[])
    {
        char* ptr = new char[20];

        delete[] ptr;
        ptr = 0;  // Comment me out and watch me crash and burn.
        delete[] ptr;
    }

    编辑:将delete更改为delete[],因为ptr是char数组。


    我知道这是一个非常老的线程,但这是Google对该错误的最高搜索,并且没有响应提及该错误的常见原因。

    这将关闭您已经关闭的文件。

    如果您不注意并且有两个不同的函数关闭同一个文件,则第二个将生成此错误。