OSは時々0xCDや0xDDなどの特定のパターンでメモリを初期化することを知っています。私が知りたいのはwhenとwhyが起こることです。
これは使用するコンパイラに固有ですか?
これに関して、malloc/newおよびfree/deleteは同じように機能しますか?
プラットフォーム固有ですか?
LinuxやVxWorksなどの他のオペレーティングシステムで発生しますか?
私の理解では、これはWin32デバッグ構成でのみ発生し、メモリオーバーランを検出し、コンパイラが例外をキャッチするために使用されます。
この初期化がどのように役立つかについて、実際的な例を挙げていただけますか?
メモリを割り当て時に既知のパターンに初期化するのが適切であり、特定のパターンがWin32で割り込みをトリガーし、デバッガーで例外が表示されるということを読んだことを覚えています(おそらくCode Complete 2で)。
これはどれくらいポータブルですか?
デバッグモード用にコンパイルされたときに、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.
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 detect 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 reproducable). 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
に変更されました。これは、たとえば呼び出し元がfread()
には大きすぎるバッファサイズを渡した場合など、フィル操作がバッファの最後を超えて実行される可能性があるためだと思います。その場合、値0xFD
はこのオーバーランの検出をトリガーしない可能性があります。バッファサイズが1つだけ大きすぎると、フィル値はそのカナリアを初期化するために使用されるno manのランド値と同じになるからです。誰の土地にも変化がないということは、オーバーランに気付かないことを意味します。
そのため、VS 2008では塗りつぶしの値が変更されたため、このような場合はノーマンのランドカナリアが変更され、ランタイムによって問題が検出されます。
他の人が指摘したように、これらの値の重要なプロパティの1つは、これらの値の1つを持つポインタ変数が逆参照されることです。標準の32ビットWindows構成ではユーザーモードアドレスは0x7fffffffよりも高くなります。
フィル値0xCCCCCCCCに関する素晴らしいプロパティの1つは、x86アセンブリでは、オペコード0xCCが int オペコードであり、これはソフトウェアブレークポイント割り込みです。そのため、その初期化されていないメモリでそのfill値で満たされたコードを実行しようとすると、すぐにブレークポイントにヒットし、オペレーティングシステムはデバッガをアタッチ(またはプロセスを強制終了)します。
コンパイラーおよびOS固有であるため、Visual Studioはさまざまな種類のメモリをさまざまな値に設定するため、デバッガーでmallocされたメモリ、固定配列、または初期化されていないオブジェクトにオーバーオーバーしたかどうかを簡単に確認できます。私がそれらをグーグルしている間に誰かが詳細を投稿します...
OSではなく、コンパイラです。動作も変更できます-この投稿の下部をご覧ください。
Microsoft Visual Studioは、スタックメモリに0xCCを事前に入力するバイナリを(デバッグモードで)生成します。また、バッファオーバーフローを検出するために、すべてのスタックフレームの間にスペースを挿入します。これが役立つ非常に簡単な例はこちらです(実際にはVisual Studioはこの問題を見つけて警告を出します)。
...
bool error; // uninitialised value
if(something)
{
error = true;
}
return error;
Visual Studioが変数を既知の値に事前初期化していない場合、このバグを見つけるのは難しい可能性があります。事前初期化された変数(または、事前初期化されたスタックメモリ)を使用すると、実行のたびに問題を再現できます。
ただし、わずかな問題があります。 Visual Studioが使用する値はTRUEです。0以外はすべてTRUEになります。実際、リリースモードでコードを実行すると、ユニタライズされた変数がたまたま0を含むスタックメモリに割り当てられる可能性があります。つまり、リリースモードでのみ現れるユニタライズされた変数のバグがある可能性があります。
それは私を悩ませたので、私は スクリプトを書いた バイナリを直接編集して事前入力値を変更し、スタックにゼロが含まれている場合にのみ現れる初期化されていない変数の問題を見つけることができました。このスクリプトは、スタックの事前入力のみを変更します。ヒープの事前入力を試したことはありませんが、可能です。ランタイムDLLの編集が必要な場合がありますが、そうでない場合があります。
これは使用するコンパイラに固有ですか?
実際、ほとんどの場合、ランタイムライブラリの機能です(Cランタイムライブラリなど)。通常、ランタイムはコンパイラと強く相関していますが、交換できる組み合わせがいくつかあります。
Windowsでは、デバッグヒープ(HeapAllocなど)も、デバッグCランタイムライブラリのmallocおよびfree実装から来るものとは異なる特別なfillパターンを使用します。そのため、OSの機能でもありますが、ほとんどの場合、それは単なる言語ランタイムライブラリです。
これに関して、malloc/newおよびfree/deleteは同じように機能しますか?
通常、newおよびdeleteのメモリ管理部分はmallocおよびfreeで実装されるため、newおよびdeleteで割り当てられたメモリは通常同じ機能を持ちます。
プラットフォーム固有ですか?
詳細はランタイム固有です。使用される実際の値は、16進ダンプを見たときに異常で明白に見えるだけでなく、プロセッサの機能を活用できる特定のプロパティを持つように設計されていることがよくあります。たとえば、アラインメントフォールトを引き起こす可能性があるため、奇数値がよく使用されます。 (0ではなく)大きな値が使用されます。これは、初期化されていないカウンターにループすると驚くほどの遅延が発生するためです。 x86では、0xCCはint 3
命令なので、初期化されていないメモリを実行すると、トラップされます。
LinuxやVxWorksなどの他のオペレーティングシステムで発生しますか?
それは主に、使用するランタイムライブラリに依存します。
この初期化がどのように役立つかについて、実際的な例を挙げていただけますか?
上記をいくつかリストしました。通常、値は、メモリの無効な部分で何かを行うと異常が発生する可能性を高めるために選択されます:長い遅延、トラップ、アライメントフォールトなど。これらのパターンが変更された場合、どこかで不正な書き込み(バッファオーバーランなど)があったことがわかります。
メモリを割り当てたときに既知のパターンにメモリを初期化するのが良いことを読んだことを覚えています(コードコンプリート2で)、特定のパターンはWin32で割り込みをトリガーし、デバッガで例外が表示されます。
これはどれくらいポータブルですか?
ソリッドコードの作成(そしておそらくコード完了)塗りつぶしパターンを選択するとき。私はここでそれらのいくつかに言及しました、そして、ウィキペディアの記事 マジックナンバー(プログラミング) はそれらを要約します。いくつかのトリックは、使用しているプロセッサーの仕様に依存します(アライメントされた読み取りと書き込みが必要かどうか、どの値がトラップする命令にマップされるかなど)。メモリダンプで際立っている大きな値や異常な値を使用するなど、他のトリックはより移植性があります。
「理由」の明白な理由は、次のようなクラスがあると仮定することです。
class Foo
{
public:
void SomeFunction()
{
cout << _obj->value << endl;
}
private:
SomeObject *_obj;
}
そして、Foo
をインスタンス化し、SomeFunction
を呼び出すと、0xCDCDCDCD
。これは、何かを初期化するのを忘れたことを意味します。それが「理由」です。そうでない場合は、ポインターが他のメモリと整列している可能性があり、デバッグがより困難になります。アクセス違反が発生した理由を知らせるだけです。このケースは非常に簡単でしたが、より大きなクラスではその間違いを犯しやすいことに注意してください。
知る限り、これは(リリースではなく)デバッグモードのVisual Studioコンパイラでのみ機能します
実行中にプロセスにデバッガーをアタッチできるため、一般的にデバッグ中にメモリが初期開始値から変更されたことが簡単にわかります。
メモリだけでなく、多くのデバッガーは、プロセスの開始時にレジスターの内容をセンチネル値に設定します(AIXのバージョンによっては、いくつかのレジスターを0xdeadbeef
これは少しユーモラスです)。
この記事では、 異常なメモリビットパターン と、これらの値に遭遇した場合に使用できるさまざまな手法について説明します。
IBM XLCコンパイラには、指定した値を自動変数に割り当てる「initauto」オプションがあります。デバッグビルドには次を使用しました。
-Wc,'initauto(deadbeef,Word)'
初期化されていない変数のストレージを見ると、0xdeadbeefに設定されます