如何使用gdb追踪C++中的double free或corruption错误
当我运行我的C++程序时,它会因此错误而崩溃。如何使用gdb追踪C++中的double free或corruption错误
* glibc的检测* ./load:双重释放或腐败(上一个!): 0x0000000000c6ed50 ***
我试图跟踪它使用的cout语句,但我发现很难。 gdb可以让这更容易吗?它是如何完成的?
至少有两种可能的情况:
- 要删除的同一个实体两次
- 要删除,这不是分配
对于第一个我强烈建议的东西将所有删除的指针置空。
你有三个选择:
- 重载new和delete和跟踪分配
- 是,使用gdb的 - 那么你会从你的崩溃得到一个回溯,这很可能会很乐于助人
- 的建议 - 使用Valgrind的 - 这是不容易进入,而且可以节省您的时间千倍在未来...
gdb是如何完成的? – neuromancer 2010-05-25 05:28:45
2.会导致腐败,但我认为这个消息通常不会出现,因为理智检查只在堆上完成。但是,我认为3.堆缓冲区溢出是可能的。 – 2010-05-25 05:31:47
好的。真的,我错过了让指针为NULL并面对这个错误。得到教训! – hrushi 2017-02-27 03:05:11
三条基本规律:
- 免费后将指针设置为NULL
- 释放前检查NULL。
- 在开始时初始化指向NULL的指针。
这三个作品的组合很好。
我不是C专家,但我通常可以保持头脑清醒。为什么#1?当你试图访问一个free'd指针,而不仅仅是一个无声的错误时,你的程序是否会崩溃? – 2010-05-25 06:48:42
@Precision:是的,就是这一点。这是一个很好的做法:有一个指向删除内存的指针是一个风险。 – ereOn 2010-05-25 08:15:32
@ereOn:谢谢。我刚刚在C上做了几个类,并且从来不必与其他人编写代码,所以从来没有真正遇到过这个问题。看起来像一个非常好的做法,但。 – 2010-05-25 08:31:08
如果您使用的glibc,你可以设置MALLOC_CHECK_
环境变量2
,这将导致的glibc使用的malloc
的容错版本,这将导致你的程序在该点中止在双重释放完成。
您可以在运行程序之前使用set environment MALLOC_CHECK_ 2
命令从gdb中设置此项;程序应该中止,在回溯中可以看到free()
的调用。
看到man page for malloc()
以获取更多信息
是否使用智能指针如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;
}
编辑:改变delete
到delete[]
,如PTR是字符数组。
我的程序中没有使用任何删除命令。这仍然是问题吗? – neuromancer 2010-05-26 01:57:19
@Phenom:你为什么不使用删除?是因为你使用智能指针吗?想必你在你的代码中使用new来在堆上创建对象?如果这两个答案都是肯定的,那么你在智能指针上使用get/set来复制原始指针吗?如果是这样,不要!你会打破引用计数。或者,您可以将您正在调用的库代码的指针分配给智能指针。如果你不'拥有'指向的内存,那么不要这样做,因为库和智能指针都会尝试删除它。 – 2010-05-27 11:23:29
您可以使用valgrind
进行调试。
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *x = malloc(100);
free(x);
free(x);
return 0;
}
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] 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
[[email protected] testbox]$
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] 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)
[[email protected] testbox]$
[[email protected] 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)
[[email protected] testbox]$
一个可能的解决办法:
#include<stdio.h>
#include<stdlib.h>
int main()
{
char *x = malloc(100);
free(x);
x=NULL;
free(x);
return 0;
}
[[email protected] testbox]$ vim t1.c
[[email protected] testbox]$ cc -g t1.c -o t1
[[email protected] testbox]$ ./t1
[[email protected] testbox]$
[[email protected] 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)
[[email protected] testbox]$
检查出使用Valgrind的Link
我知道这是一个非常古老的线程,但它是这个错误的顶级的谷歌搜索的博客,没有一个答复提到了这个错误的常见原因。
哪个关闭已关闭的文件。
如果你不注意并且有两个不同的函数关闭同一个文件,那么第二个会产生这个错误。
使用现代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
任何解决方案me.So没有工作,我开始乱搞找到一种方法,这为我工作的: 错误是显示每次我:
MyClass b(5);
MyClass a = b;
我改变了代码:
MyClass b(5);
MyClass a;
a = b;
它只是为我工作,希望它也适用于你。
我不知道为什么每个人都建议'NULL'指针(它掩盖了被捕获的错误,正如这个问题很好地显示的那样),但是没有人建议完全不做手动内存管理,这在C++中是非常好的。多年以来我没有写过'删除'。 (而且,是的,我的代码对性能至关重要,否则它不会用C++编写)。 – sbi 2010-05-25 08:32:15
@sbi:堆腐败等很少被捕获,至少不是它们发生的地方。 NULL指针可能会让你的程序更早崩溃。 – Hasturkun 2010-05-25 08:45:14
@Hasturkun:我非常不同意。 “NULL”指针的一个主要动机是防止第二个'delete ptr;'被炸毁 - 这掩盖了一个错误,因为第二个'delete'不应该发生。 (它也用来检查一个指针是否仍然指向一个有效的对象,但这只是提出了一个问题,为什么你有一个没有指向对象的指针) – sbi 2010-05-25 08:55:56