いつ、なぜコンパイラはmalloc / free / new / deleteでメモリを0xCD、0xDDなどに初期化しますか?


129

コンパイラが、0xCDやなどの特定のパターンでメモリを初期化する場合があることを知ってい0xDDます。私が知りたいのは、いつなぜこれが起こるのかということです。

いつ

これは使用するコンパイラに固有ですか?

やるmalloc/newfree/deleteこれに関連して同じように動作しますか?

プラットフォーム固有ですか?

Linuxまたはなどの他のオペレーティングシステムで発生しVxWorksますか?

なぜ

私の理解では、これはWin32デバッグ構成でのみ発生し、メモリオーバーランを検出し、コンパイラが例外をキャッチするのに役立ちます。

この初期化がどのように役立つかについて、実用的な例を挙げていただけますか?

割り当て時にメモリを既知のパターンに初期化することをお勧めします(コードコンプリート2で)何かを読んだことを覚えていWin32ます。特定のパターンは割り込みをトリガーし、デバッガーに例外が表示されます。

これはどれほどポータブルですか?

回答:


191

デバッグモード用にコンパイルされたときに、Microsoftのコンパイラが所有していない/初期化されていないメモリのさまざまなビットに使用するものの概要(サポートはコンパイラのバージョンによって異なる場合があります):

Value     Name           Description 
------   --------        -------------------------
0xCD     Clean Memory    Allocated memory via malloc or new but never 
                         written by the application. 

0xDD     Dead Memory     Memory that has been released with delete or free. 
                         It is used to detect writing through dangling pointers. 

0xED or  Aligned Fence   'No man's land' for aligned allocations. Using a 
0xBD                     different value here than 0xFD allows the runtime
                         to detect not only writing outside the allocation,
                         but to also identify mixing alignment-specific
                         allocation/deallocation routines with the regular
                         ones.

0xFD     Fence Memory    Also known as "no mans land." This is used to wrap 
                         the allocated memory (surrounding it with a fence) 
                         and is used to detect indexing arrays out of 
                         bounds or other accesses (especially writes) past
                         the end (or start) of an allocated block.

0xFD or  Buffer slack    Used to fill slack space in some memory buffers 
0xFE                     (unused parts of `std::string` or the user buffer 
                         passed to `fread()`). 0xFD is used in VS 2005 (maybe 
                         some prior versions, too), 0xFE is used in VS 2008 
                         and later.

0xCC                     When the code is compiled with the /GZ option,
                         uninitialized variables are automatically assigned 
                         to this value (at byte level). 


// the following magic values are done by the OS, not the C runtime:

0xAB  (Allocated Block?) Memory allocated by LocalAlloc(). 

0xBAADF00D Bad Food      Memory allocated by LocalAlloc() with LMEM_FIXED,but 
                         not yet written to. 

0xFEEEFEEE               OS fill heap memory, which was marked for usage, 
                         but wasn't allocated by HeapAlloc() or LocalAlloc(). 
                         Or that memory just has been freed by HeapFree(). 

免責事項:表は私がうそをついているいくつかのメモからのものです-それらは100%正しい(または一貫性のある)ものではないかもしれません。

これらの値の多くは、vc / crt / src / dbgheap.cで定義されています。

/*
 * The following values are non-zero, constant, odd, large, and atypical
 *      Non-zero values help find bugs assuming zero filled data.
 *      Constant values are good, so that memory filling is deterministic
 *          (to help make bugs reproducible).  Of course, it is bad if
 *          the constant filling of weird values masks a bug.
 *      Mathematically odd numbers are good for finding bugs assuming a cleared
 *          lower bit.
 *      Large numbers (byte values at least) are less typical and are good
 *          at finding bad addresses.
 *      Atypical values (i.e. not too often) are good since they typically
 *          cause early detection in code.
 *      For the case of no man's land and free blocks, if you store to any
 *          of these locations, the memory integrity checker will detect it.
 *
 *      _bAlignLandFill has been changed from 0xBD to 0xED, to ensure that
 *      4 bytes of that (0xEDEDEDED) would give an inaccessible address under 3gb.
 */

static unsigned char _bNoMansLandFill = 0xFD;   /* fill no-man's land with this */
static unsigned char _bAlignLandFill  = 0xED;   /* fill no-man's land for aligned routines */
static unsigned char _bDeadLandFill   = 0xDD;   /* fill free objects with this */
static unsigned char _bCleanLandFill  = 0xCD;   /* fill new objects with this */

また、デバッグランタイムがバッファ(またはバッファの一部)を既知の値(たとえば、std::stringの割り当ての「スラック」スペースやに渡されるバッファ)で満たすこともありますfread()。これらのケースでは、名前_SECURECRT_FILL_BUFFER_PATTERN(で定義crtdefs.h)が指定された値を使用します。いつ導入されたのかは正確にはわかりませんが、少なくともVS 2005(VC ++ 8)ではデバッグランタイムに含まれていました。

当初、これらのバッファーを埋めるために使用された値は、0xFD人間のいない土地に使用されたのと同じ値でした。ただし、VS 2008(VC ++ 9)では、値がに変更されました0xFE。たとえば、呼び出し元が大きすぎるバッファサイズを渡した場合など、fill操作がバッファの最後を超えて実行される可能性があるためと考えられfread()ます。その場合、0xFDバッファサイズが1だけ大きすぎると、フィル値はそのカナリアの初期化に使用された無人の土地値と同じになるため、この値はこのオーバーランの検出をトリガーしない可能性があります。人のいない土地の変化がないということは、オーバーランに気付かないということです。

そのため、VS 2008では塗りつぶしの値が変更されたので、そのような場合は無人のカナリアが変更され、ランタイムによって問題が検出されました。

他の人が指摘したように、これらの値の主要なプロパティの1つは、これらの値の1つを持つポインター変数が逆参照されると、アクセス違反が発生することです。これは、標準の32ビットWindows構成では、ユーザーモードアドレスが0x7fffffffより高くなることはありません。


1
それがMSDNにあるかどうかはわかりません-あちこちからつなぎ合わせたか、他のWebサイトから入手した可能性があります。
マイケルバー

2
そうそう、その一部はDbgHeap.cのCRTソースからのものです。
マイケル・バー、

その一部はMSDN(msdn.microsoft.com/en-us/library/bebs9zyz.aspx)にありますが、すべてではありません。良いリスト。
ショーンe '22

3
@seane-ちなみにあなたのリンクは死んでいるようです。新しいもの(テキストが拡張されています)はこちらから入手できます:msdn.microsoft.com/en-us/library/974tc9t1.aspx
Simon Mourier '19

これらのブロックの名前は何ですか?メモリバリア、membar、メモリフェンス、またはフェンスの指示(en.wikipedia.org/wiki/Memory_barrier)ですか?
kr85

36

フィル値0xCCCCCCCCの1つの優れた特性は、x86アセンブリでは、オペコード0xCCがソフトウェアブレークポイント割り込みであるint3オペコードであることです。したがって、その初期化されていないメモリで、その値で満たされたコードを実行しようとすると、すぐにブレークポイントに到達し、オペレーティングシステムがデバッガーをアタッチ(またはプロセスを強制終了)します。


6
また、0xCDはint命令なので、0xCD 0xCDを実行するint CDと、トラップも生成されるが生成されます。
Tad Marshall

2
今日の世界では、データ実行防止では、CPUがヒープから命令をフェッチすることさえできません。この回答はXP SP2以降は古くなっています。
MSalters 2017

2
@MSalters:はい、デフォルトでは、新しく割り当てられたメモリは実行不可になりますが、誰かが簡単にメモリを使用しVirtualProtect()たりmprotect()実行可能にしたりできます。
Adam Rosenfield 2017

データブロックからコードを実行することはできません。これまで。再び推測。
Dan

9

コンパイラとOS固有であり、Visual Studioはさまざまな種類のメモリをさまざまな値に設定するので、デバッガでmallocされたメモリ、固定配列、または初期化されていないオブジェクトに行き過ぎているかどうかを簡単に確認できます。私がそれらをググっている間、誰かが詳細を投稿します...

http://msdn.microsoft.com/en-us/library/974tc9t1.aspx


私の推測では、文字列を適切に終了するのを忘れたかどうかを確認するためにも使用されます(これらの0xCDまたは0xDDが出力されるため)。
ストレッジャー2008

0xCC =初期化されていないローカル(スタック)変数0xCD =初期化されていないクラス(ヒープ?)変数0xDD =削除された変数
FryGuy

@FryGuy ここで説明するように、これらの値(の一部)を指定する実際的な理由があります
Glenn Slayden 2018年

4

これはOSではなく、コンパイラです。動作も変更できます。この投稿の下部をご覧ください。

Microsoft Visual Studioは、(デバッグモードで)スタックメモリを0xCCで事前に埋めるバイナリを生成します。また、バッファオーバーフローを検出するために、各スタックフレームの間にスペースを挿入します。これが役立つ場所の非常に単純な例を次に示します(実際には、Visual Studioはこの問題を見つけて警告を発行します)。

...
   bool error; // uninitialised value
   if(something)
   {
      error = true;
   }
   return error;

Visual Studioが変数を既知の値に事前初期化しなかった場合、このバグを見つけるのは難しい可能性があります。事前に初期化された変数(またはむしろ、事前に初期化されたスタックメモリ)を使用すると、問題はすべての実行で再現可能です。

ただし、若干の問題があります。Visual Studioが使用する値はTRUEです。0以外は何でもかまいません。実際には、リリースモードでコードを実行すると、ユニティライズされた変数がたまたま0を含むスタックメモリに割り当てられる可能性が高く、これは、リリースモードでのみ現れるユニティライズされた変数のバグが発生する可能性があることを意味します。

それは私を困らせたので、バイナリを直接編集して事前入力値を変更するスクリプト作成しました。これにより、スタックにゼロが含まれている場合にのみ現れる初期化されていない変数の問題を見つけることができます。このスクリプトはスタックの事前入力のみを変更します。ヒーププレフィルを試すことはできませんでしたが、可能です。ランタイムDLLの編集が含まれる場合がありますが、含まれない場合があります。


1
GCCのように、初期化される前の値を使用すると、VSは警告を発行しませんか?
ストレッジャー2008

3
はい、ただし常にではありません。静的分析に依存しているためです。その結果、ポインタ演算と混同することは非常に簡単です。
Airsource Ltd

3
「OSではなく、コンパイラです。」実際、これはコンパイラではなく、ランタイムライブラリです。
エイドリアン・マッカーシー

デバッグ時に、Visual Studioデバッガーは、trueまたは(204)のようなもので0または1でない場合はブール値を表示します。そのため、コードをトレースすると、この種のバグが比較的簡単にわかります。
Phil1970、19年

4

これは使用するコンパイラに固有ですか?

実際、ほとんどの場合、これはランタイムライブラリの機能です(Cランタイムライブラリなど)。ランタイムは通常コンパイラと強く相関していますが、入れ替え可能な組み合わせがいくつかあります。

Windowsでは、デバッグヒープ(HeapAllocなど)も、デバッグCランタイムライブラリのmallocおよびfree実装からのパターンとは異なる特別な塗りつぶしパターンを使用していると思います。したがって、これはOSの機能である可能性もありますが、ほとんどの場合、それは単なる言語ランタイムライブラリです。

malloc / newとfree / deleteはこれに関して同じように機能しますか?

newとdeleteのメモリ管理部分は通常mallocとfreeで実装されるため、newとdeleteで割り当てられたメモリは通常同じ機能を備えています。

プラットフォーム固有ですか?

詳細はランタイム固有です。使用される実際の値は、多くの場合、16進ダンプを見たときに異常で明白に見えるだけでなく、プロセッサの機能を利用できる特定のプロパティを持つように選択されています。たとえば、奇数の値は、アライメントフォールトを引き起こす可能性があるため、よく使用されます。初期化されていないカウンターにループすると驚くほどの遅延が発生するため、(0ではなく)大きな値が使用されます。x86では、0xCCがint 3命令なので、初期化されていないメモリを実行すると、トラップされます。

LinuxやVxWorksなどの他のオペレーティングシステムで発生しますか?

これは主に、使用するランタイムライブラリに依存します。

この初期化がどのように役立つかについて、具体的な例を挙げていただけますか?

上記にいくつか挙げました。値は通常、メモリの無効な部分(長い遅延、トラップ、アライメントエラーなど)で何かを行うと異常が発生する可能性を高めるために選択されます。ヒープマネージャーは、割り当て間のギャップに特別な塗りつぶし値を使用することもあります。これらのパターンが変化する場合、どこかに悪い書き込み(バッファオーバーランなど)があったことがわかります。

割り当て時にメモリを既知のパターンに初期化することをお勧めします(コードコンプリート2の可能性があります)。Win32では、特定のパターンが割り込みをトリガーし、デバッガーに例外が表示されます。

これはどれほどポータブルですか?

充実したコードを書く(そして多分Code Complete)は、塗りつぶしパターンを選択するときに考慮すべきことについて話します。それらのいくつかをここで述べましたが、マジックナンバー(プログラミング)に関するWikipediaの記事もそれらを要約しています。いくつかのトリックは、使用しているプロセッサの詳細に依存します(たとえば、整列した読み取りと書き込みが必要かどうか、どの値がトラップする命令にマッピングされるかなど)。メモリダンプで目立つ大きな値や異常な値を使用するなど、他のトリックはより移植性があります。



2

「なぜ」の明白な理由は、次のようなクラスがあると仮定することです。

class Foo
{
public:
    void SomeFunction()
    {
        cout << _obj->value << endl;
    }

private:
    SomeObject *_obj;
}

そして、あなたは1つをインスタンス化してFooを呼び出しますSomeFunction、それは読み取りを試みているアクセス違反を与えます0xCDCDCDCD。これは、何かを初期化するのを忘れたことを意味します。それが「なぜなのか」です。そうでない場合は、ポインタが他のメモリと並んでいる可能性があり、デバッグが困難になります。アクセス違反が発生した理由を通知するだけです。このケースはかなり単純でしたが、より大きなクラスでは、間違いを犯しやすいことに注意してください。

私の知る限り、これはデバッグモード(リリースではなく)の場合にのみVisual Studioコンパイラで機能します。


また、を読み込もうとするアクセス違反が発生するため、説明は適用されません0x00000000。このページの別のコメントで指摘したように、0xCD(および0xCC)の本当の理由は、ソフトウェア割り込みをトリガーする解釈可能なx86オペコードであり、これにより、1つの特定のまれなタイプのエラーでデバッガに正常に回復できることです。つまり、CPUが誤って非コード領域でバイトを実行しようとした場合。この機能的な使用を除いて、フィルの値は、注意点にすぎません。
Glenn Slayden 2018年

2

プロセスの実行中にデバッガーをプロセスにアタッチできるため、メモリが初期の開始値から変更されたことを簡単に確認できます。

メモリだけでなく、多くのデバッガーはプロセスの開始時にレジスターの内容をセンチネル値に設定します(AIXの一部のバージョンで0xdeadbeefは、軽度にユーモラスな一部のレジスターを設定します)。


1

IBM XLCコンパイラには、自動変数に指定した値を割り当てる「initauto」オプションがあります。私はデバッグビルドに以下を使用しました:

-Wc,'initauto(deadbeef,word)'

初期化されていない変数のストレージを見ると、0xdeadbeefに設定されています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.