立即检测Windows上的堆损坏错误.怎么样?

Ser*_* K. 14 c c++ heap-memory virtualalloc heap-corruption

我睡不着!:)

我在Windows上有一个相当大的项目,遇到了一些堆损坏问题.我已阅读所有SO,包括这个好主题:如何调试堆损坏错误?但是没有什么比开箱即用更能帮助我了.Debug CRTBoundsChecker检测到堆损坏,但地址总是不同的,并且检测点总是远离实际存储器重写.我没有睡到半夜,并制作了以下黑客:

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);
}
Run Code Online (Sandbox Code Playgroud)

一些堆损坏错误变得明显,我能够解决它们.退出时不再有Debug CRT警告.但是,我对这个黑客有一些疑问:

1.它会产生任何误报吗?

2.它会错过一些堆损坏的?(即使我们替换malloc/realloc/free?)

3.它无法在32位上运行OUT_OF_MEMORY,仅在64位上运行.我是对的,我们只是用完了32位的虚拟地址空间?

Ale*_*nze 8

它会产生任何误报吗?

因此,这只会捕获类"free after()之后的错误".为此,我认为,这是相当不错的.

如果你尝试的delete东西不是new'ed,那就是一种不同类型的bug.在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;
Run Code Online (Sandbox Code Playgroud)

它无法在具有OUT_OF_MEMORY错误的32位目标上运行,仅在64位上运行.我是对的,我们只是用完了32位的虚拟地址空间?

通常,您在32位Windows上可以获得大约2GB的虚拟地址空间.new在提供的代码中,这最好~524288 .但是对于大于4KB的对象,您将能够成功分配更少的实例.然后地址空间碎片将进一步减少这个数字.

如果在程序的生命周期中创建许多对象实例,那么这是一个完美的预期结果.

  • 谢谢你的详细说明!先生,很高兴在这里见到你!我从你的pascal/asm 3D引擎开始学习3D图形:) (2认同)

Use*_*ess 6

这不会抓住:

  • 使用未初始化的内存(一旦你的指针被分配,你可以随意读取垃圾)
  • 缓冲区溢出(除非你超出PageSize边界)

理想情况下,您应该在分配的块之前和之后编写一个众所周知的位模式,以便operator delete检查它们是否被覆盖(指示缓冲区过度运行或运行不足).

目前,这将在您的方案中以静默方式允许,并且切换回malloc等将允许它静默地损坏堆,并且稍后显示为错误(例如,在过度运行之后释放块时).

你无法捕捉到所有内容:例如,请注意,如果底层问题是(有效)指针被垃圾覆盖某处,则在损坏的指针被取消引用之前,您无法检测到这一点.

  • 真的,我认为您要么需要访问硬件观察点,要么需要一些聪明的内存保护技巧(理想情况下是处理陷阱的能力).另一方面,Valgrind基本上通过_interpreting_程序来实现它,因此它可以拦截每次读写. (2认同)