14

眠れません!:)

Windows でかなり大きなプロジェクトを実行しており、ヒープ破損の問題が発生しました。この素敵なトピックを含むすべての SO を読みました:ヒープ破損エラーをデバッグするには? 、しかし、すぐに使用できるものは何もありませんでした。Debug CRTヒープのBoundsChecker破損が検出されましたが、アドレスは常に異なり、検出ポイントは常に実際のメモリの上書きから遠く離れていました。私は真夜中まで寝ていなかったので、次のハックを作成しました。

DWORD PageSize = 0;

inline void SetPageSize()
{
    if ( !PageSize )
    {
        SYSTEM_INFO sysInfo;
        GetSystemInfo(&sysInfo);
        PageSize = sysInfo.dwPageSize;
    }
}

void* operator new (size_t nSize)
{
    SetPageSize();
    size_t Extra = nSize % PageSize;
    nSize = nSize + ( PageSize - Extra );
    return Ptr = VirtualAlloc( 0, nSize, MEM_COMMIT, PAGE_READWRITE);
}

void operator delete (void* pPtr)
{
    MEMORY_BASIC_INFORMATION mbi;
    VirtualQuery(pPtr, &mbi, sizeof(mbi));
    // leave pages in reserved state, but free the physical memory
    VirtualFree(pPtr, 0, MEM_DECOMMIT);
    DWORD OldProtect;
    // protect the address space, so noone can access those pages
    VirtualProtect(pPtr, mbi.RegionSize, PAGE_NOACCESS, &OldProtect);
}

いくつかのヒープ破損エラーが明らかになり、修正することができました。終了時に Debug CRT 警告が表示されなくなりました。ただし、このハックに関していくつか質問があります。

1.誤検知を引き起こす可能性はありますか?

2.ヒープの破損を見逃すことはありますか? (malloc/realloc/free を置き換えても?)

3. 32 ビットでは実行できOUT_OF_MEMORYず、64 ビットでのみ実行されます。32ビットの仮想アドレス空間を使い果たしただけでいいのでしょうか?

4

3 に答える 3

8

誤検知を引き起こす可能性はありますか?

したがって、これは「use after free()」クラスのバグのみをキャッチします。そのためには、かなり良いと思います。

delete編集されていないことを試みた場合new、それは別の種類のバグです。まずdelete、メモリが実際に割り当てられているかどうかを確認する必要があります。やみくもにメモリを解放して、アクセス不可としてマークするべきではありません。私はそれを避けて、(たとえば、デバッグ ブレークを行うことによって) 報告しようとしdeleteますnew

ヒープの破損を見逃すことはありますか? (malloc/realloc/free を置き換えても?)

明らかに、これは とnewの間のヒープ データのすべての破損をキャッチするわけではありませんdelete。の後に試行されたもののみをキャッチしdeleteます。

例えば:

myObj* = new MyObj(1,2,3);
// corruption of *myObj happens here and may go unnoticed
delete myObj;

OUT_OF_MEMORY エラーで 32 ビット ターゲットでは実行できず、64 ビットでのみ実行されます。32 ビットの仮想アドレス空間を単純に使い果たしたというのは正しいですか?

通常、32 ビット Windows では、約 2 GB の仮想アドレス空間を利用できます。newこれは、提供されたコードのように、最大​​で ~524288 に適しています。ただし、4KB を超えるオブジェクトでは、それよりも少ないインスタンスを正常に割り当てることができます。そして、アドレス空間の断片化により、その数がさらに減少します。

プログラムのライフ サイクル中に多くのオブジェクト インスタンスを作成する場合、これは完全に予想される結果です。

于 2012-10-04T09:54:29.590 に答える
6

これはキャッチしません:

  • 初期化されていないメモリの使用 (ポインタが割り当てられると、そこからガベージを自由に読み取ることができます)
  • バッファ オーバーラン (PageSize 境界をオーバーランしない限り)

理想的には、割り当てられたブロックの前後に既知のビット パターンを書き込む必要があります。これにより、ブロックoperator deleteが上書きされたかどうかを確認できます (バッファーのオーバーランまたはアンダーランを示します)。

malloc現在、これはスキームで黙って許可されており、 etc に戻すとヒープが黙って損傷し、後でエラーとして表示されます (たとえば、オーバーランしたブロックの後にブロックを解放するとき)。

ただし、すべてをキャッチすることはできません。たとえば、根本的な問題が (有効な) ポインターのどこかがガベージで上書きされている場合、破損したポインターが参照解除されるまでこれを検出できないことに注意してください。

于 2012-10-04T09:36:46.063 に答える