「ダブルフリーまたは破損」エラーを追跡する方法


95

(C ++)プログラムを実行すると、このエラーでクラッシュします。

* glibcが検出されました*。 / load:ダブルフリーまたは破損(!prev):0x0000000000c6ed50 ***

エラーを追跡するにはどうすればよいですか?

print(std::cout)ステートメントを使用しようとしましたが、成功しませんでした。でしたgdbこれを簡単にしますか?


5
なぜ誰もがNULLポインタを提案するのだろうか(この質問がうまく示すように、他の方法でキャッチされるエラーをマスクします)、C ++では非常に可能である手動のメモリ管理をまったく行わないことを提案する人は誰もいません。私は何delete年も書いていません。(そして、はい、私のコードはパフォーマンスが重要です。そうでなければ、C ++で記述されていなかったでしょう。)
sbi 2010年

2
@sbi:ヒープの破損などは、少なくとも発生した場所では、めったに検出されません。NULLポインタを使用すると、プログラムが早くクラッシュする可能性があります。
Hasturkun 2010年

@Hasturkun:私は強く反対します。NULLポインタへの主なインセンティブは、秒delete ptr;が爆発するのを防ぐdeleteことです。これは、エラーをマスクすることです。その秒は決して発生してはならないからです。(これは、ポインターがまだ有効なオブジェクトを指しているかどうかを確認するためにも使用されます。しかし、それは、ポイントするオブジェクトがないスコープ内にポインターがある理由を疑問視するだけです。)
sbi 2010年

回答:


64

glibcを使用している場合は、MALLOC_CHECK_環境変数をに設定できます。2これにより、glibcはエラー許容バージョンのを使用しmalloc、ダブルフリーが実行された時点でプログラムが中止されます。

set environment MALLOC_CHECK_ 2プログラムを実行する前にコマンドを使用して、gdbからこれを設定できます。プログラムは中止され、free()呼び出しはバックトレースに表示されます。

詳細については、manページをmalloc()参照してください。


2
設定MALLOC_CHECK_2(これはデバッグモードでのみだ場合、それは固定ではないですが)、実際に固定私のダブルフリーの問題を
PUK

4
@puk同じ問題があります。MALLOC_CHECK_を2に設定すると、ダブルフリーの問題を回避できます。問題を再現し、バックトレースを提供するために、コードほど少なく注入する他のオプションはありますか?
魏忠

また、MALLOC_CHECK_を設定することで問題が回避される場合にも使用できます。仲間のコメンテーター/誰か...問題を示す別の方法を見つけましたか?
pellucidcoder

「MALLOC_CHECK_がゼロ以外の値に設定されている場合、同じ引数を使用したfreeの二重呼び出しや、1バイトのオーバーラン(オフ)などの単純なエラーに耐えるように設計された特別な(効率の低い)実装が使用されます。 -by-oneバグ)。」gnu.org/software/libc/manual/html_node/…したがって、MALLOC_CHECK_は単純なメモリエラーを回避するためにのみ使用され、それらを検出するためには使用されていないようです。
pellucidcoder

実際には.... support.microfocus.com/kb/doc.php?id=3113982 MALLOC_CHECK_を3に設定するのが最も便利で、エラーの検出に使用できるようです。
pellucidcoder

33

考えられる状況は少なくとも2つあります。

  1. 同じエンティティを2回削除しています
  2. 割り当てられていないものを削除しています

最初のものについては、削除されたすべてのポインタをNULLにすることを強くお勧めします。

3つのオプションがあります。

  1. newをオーバーロードし、割り当てを削除して追跡します
  2. はい、gdbを使用します-そうすると、クラッシュからバックトレースが得られます。これはおそらく非常に役立ちます
  3. 提案されているように-Valgrindを使用してください-入るのは簡単ではありませんが、将来的には千倍の時間を節約できます...

2.破損が発生しますが、サニティチェックはヒープ上でのみ行われるため、このメッセージは通常表示されないと思います。ただし、3。ヒープバッファオーバーフローが発生する可能性があると思います。
Matthew Flaschen 2010年

良いもの。本当私はポインタをNULLにするのを逃し、このエラーに直面しました。学んだ教訓!
hrushi 2017

26

gdbを使用できますが、最初にValgrindを試してみます。クイックスタートガイドを参照してください

簡単に言うと、Valgrindはプログラムをインストルメント化して、動的に割り当てられたメモリを使用する際のいくつかの種類のエラーを検出できるようにします。エラーが発生するとすぐに検出して報告する ため、問題の原因を直接指摘できます。


1
@SMR、この場合、答えの本質的な部分は、大きなリンクされたページ全体です。したがって、回答にリンクだけを含めることはまったく問題ありません。著者がgdbよりもValgrindを好む理由と、彼が特定の問題にどのように取り組むかについてのいくつかの言葉は、私見ですが、答えには本当に欠けています。
ndemou 2016年

20

3つの基本的なルール:

  1. ポインタをNULL解放後に設定する
  2. NULL解放する前に確認してください。
  3. NULL最初にポインタを初期化します。

これら3つの組み合わせは非常にうまく機能します。


1
私はCの専門家ではありませんが、通常は頭を水上に保つことができます。なぜ#1?サイレントエラーだけでなく、解放されたポインタにアクセスしようとすると、プログラムが完全にクラッシュするのでしょうか。
ダニエルハームズ2010年

1
@Precision:はい、それがポイントです。これは良い習慣です。削除されたメモリへのポインタを持つことはリスクです。
ereOn 2010年

10
厳密に言えば、ほとんどのコンパイラでは問題を引き起こさずにnullポインタを削除しようとするため、#2は不要だと思います。私が間違っていれば誰かが私を訂正すると確信しています。:)
コンポーネント10

11
@ Component10 C標準では、何もしないためにNULLを解放する必要があると思います。
デミ

2
@Demetri:はい、その通りです。「deleteのオペランドの値がnullポインターの場合、操作は効果がありません」。(ISO / IEC 14882:2003(E)5.3.5.2)
コンポーネント10

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

1つの可能な修正:

#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の使用上のブログをチェックアウトリンク


私のプログラムの実行には約30分かかりますが、Valgrindでは終了するのに18〜20時間かかる場合があります。
KeminZhou19年

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

編集:ptrはcharの配列であるdeleteためdelete[]、に変更されました。


プログラムで削除コマンドを使用しませんでした。これはまだ問題でしょうか?
ニューロマンサー2010年

1
@Phenom:なぜ削除を使用しなかったのですか?スマートポインタを使用しているからですか?おそらく、ヒープ上にオブジェクトを作成するためにコードでnewを使用していますか?これらの両方に対する答えが「はい」の場合、スマートポインターでget / setを使用して、生のポインターをコピーしていますか?もしそうなら、しないでください!あなたは参照カウントを破るでしょう。または、呼び出しているライブラリコードからスマートポインタにポインタを割り当てることもできます。指し示したメモリを「所有」していない場合は、ライブラリとスマートポインタの両方がそれを削除しようとするため、それを行わないでください。
コンポーネント10

-2

私はこれが非常に古いスレッドであることを知っています、しかしそれはこのエラーのトップグーグル検索であり、そしてどの応答もエラーの一般的な原因に言及していません。

すでに閉じているファイルを閉じています。

注意を払っていない場合、2つの異なる関数が同じファイルを閉じると、2番目の関数がこのエラーを生成します。


あなたは間違っています、このエラーは、エラーが述べているように、ダブルフリーのためにスローされます。ファイルを2回閉じているという事実は、closeメソッドが同じデータを2回解放しようとしていることは明らかであるため、doublefreeを引き起こしています。
ジェフリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.