如何追踪“双重释放或腐败”错误


95

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

*检测到glibc * ./load:两次释放或损坏(!prev):0x0000000000c6ed50 ***

如何追踪错误?

我尝试使用print(std::cout)语句,但未成功。可以gdb简化这个过程?


5
我不知道为什么每个人都建议使用NULL指针(如本问题很好地说明的那样,它掩盖了否则会捕获的错误),但是没有人建议根本不做任何手动内存管理,这在C ++中是非常有可能的。我好几年没写delete了。(而且,是的,我的代码对性能至关重要。否则,就不会用C ++编写。)
sbi 2010年

2
@sbi:堆腐败之类的东西很少被发现,至少不是在它们发生的地方。NULLing指针可能会使您的程序更早崩溃。
Hasturkun 2010年

@Hasturkun:我非常不同意。NULL指针的主要诱因是防止秒delete ptr;的爆炸-这掩盖了错误,因为该秒delete永远都不会发生。(它还用于检查指针是否仍指向有效对象。但这仅提出了一个问题,为什么您的指针在作用域中没有要指向的对象。)
sbi 2010年

Answers:


64

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

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

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


2
设置MALLOC_CHECK_2实际上解决了我的双重释放问题(尽管仅在调试模式下
无法解决

4
@puk我有同样的问题,将MALLOC_CHECK_设置为2确实可以避免我的双重释放问题。除了注入代码以重现问题并提供回溯外,还有哪些其他选择?
魏中

也可以通过设置MALLOC_CHECK_避免此问题。各位评论者/其他人...您是否想出了另一种方式来表现问题?
pellucidcoder

“当MALLOC_CHECK_设置为非零值时,将使用特殊(效率较低)的实现,该实现旨在容忍简单错误,例如使用相同参数两次调用free或单字节超限(关闭-一个错误)。” gnu.org/software/libc/manual/html_node/…因此,似乎MALLOC_CHECK_仅用于避免简单的内存错误,而不是检测到它们。
pellucidcoder

实际上.... support.microfocus.com/kb/doc.php?id=3113982似乎将MALLOC_CHECK_设置为3是最有用的,可用于检测错误。
pellucidcoder

33

至少有两种可能的情况:

  1. 您两次删除同一实体
  2. 您正在删除未分配的内容

对于第一个,我强烈建议对所有已删除的指针进行NULL处理。

您有三种选择:

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

2.会导致损坏,但是我认为一般不会出现此消息,因为完整性检查仅在堆上完成。但是,我认为3.堆缓冲区溢出是可能的。
马修·弗拉申

好一个 正确,我错过了使指针为NULL的问题,并遇到了此错误。得到教训!
hrushi

26

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

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


1
@SMR,在这种情况下,答案的关键部分是整个大型链接页面。因此,仅在答案中包含链接就可以了。关于作者为何更喜欢Valgrind而不是gdb以及他将如何解决特定问题的几句话是恕我直言,答案中真正缺少的是什么。
ndemou,2016年

20

三个基本规则:

  1. NULL释放后将指针设置为
  2. NULL释放前检查。
  3. 首先初始化指向的指针NULL

这三者的结合效果很好。


1
我不是C专家,但是我通常可以保持头脑清醒。为什么是#1?仅仅是这样,当您尝试访问释放指针而不仅仅是静默错误时,您的程序就崩溃了吗?
丹尼尔·哈姆斯

1
@Precision:是的,这就是重点。这是一个好习惯:有一个指向已删除内存的指针是一种风险。
ereOn 2010年

10
严格来讲,我认为#2是不必要的,因为大多数编译器将允许您尝试删除空指针而不会引起问题。我敢肯定,如果我错了,有人会纠正我的。:)
组件10年

11
@ Component10我认为C标准要求不执行NULL释放操作。
黛咪(Demi)

2
@Demetri:是的,您说对了:“如果delete操作数的值为空指针,则该操作无效。” (ISO / IEC 14882:2003(E)5.3.5.2)
组件

16

您可以valgrind用来调试它。

#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]$

一种可能的解决方法:

#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的博客


我的程序运行大约需要30分钟,在Valgrind上可能需要18到20个小时才能完成。
Kemin Zhou

13

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

示例示例:

我的程序:

$cat d_free.cxx 
#include<iostream>

using namespace std;

int main()

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

用地址清理器进行编译:

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

执行 :

# ./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等)的文档。


5

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

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

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

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;
}

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


我在程序中未使用任何删除命令。这仍然是问题吗?
Neuromancer 2010年

1
@Phenom:为什么不使用删除?是因为您正在使用智能指针吗?大概您在代码中使用new在堆上创建对象?如果这两个答案都是肯定的,那么您是否在智能指针上使用get / set围绕原始指针进行复制?如果是这样,那就不要!您将打破参考计数。或者,您可以从要调用的库代码中为智能指针分配一个指针。如果您不“拥有”指向的内存,请不要这样做,因为库和智能指针都将尝试删除它。
组件10年

-2

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

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

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


您是不正确的,完全按照错误状态说明,该错误是由于double free引发的。您两次关闭文件的事实会导致两次释放,因为close方法显然试图将同一数据两次释放。
杰弗里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.