VC调试版本C运行库内存申请的一个bug(转)

    技术2022-05-14  18

     

    遇到过一个通信方面的软件,需要长期运行,做压力测试时,高负荷连续运行一定天数时必定崩溃,而且都是在msvcrtd.dll中崩溃。负责维护的人百思不得其解,就去问微软的人,结果微软的人说这是VC6带的msvcrtd.dll的一个问题,VC2005已经没有这个问题了,请升级到新的版本。这个软件规模比较大,依赖于很多库,后台都是用VC6编译的调试版本,为了方便定位问题,没有Release版本。升级到VC2005后会不会出现别的问题,没有人敢冒这个风险,于是没有使用VC2005

     

    闲着没事的时候分析了一下,才发现问题其实很简单。msvcrtd.dll对每次内存申请都进行计数,当计数值达到设定的某个值时,就会调用_CrtDbgBreak()MSDN_CrtDbgBreak的说明是:Sets a break point on a particular line of code,其实_CrtDbgBreakX86下只有一条指令就是int 3(0xCC)

    dbgheap.c中定义了下面两个变量:

    static long _lRequestCurr = 1;      /* Current request number */

    extern "C" _CRTIMP long _crtBreakAlloc = -1L;  /* Break on allocation by request number */

    _lRequestCurr表示当前的申请次数,_crtBreakAlloc表示当内存申请次数达到某个值时break,即调用_CrtDbgBreak。详情可参考debugheap.c中的_heap_alloc_dbg_impl函数:

    lRequest = _lRequestCurr;

    /* break into debugger at specific memory allocation */

    if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)

     _CrtDbgBreak();

    VC6附带的dbgheap.c中没有添加_crtBreakAlloc != -1L的判断,而是:

    if (lRequest == _crtBreakAlloc)

     _CrtDbgBreak();

    _lRequestCurr初始化为1,每次申请内存都加1,当_lRequestCurr-1时在VC6dbgheap.c中就会触发int 3导致程序退出,而在新的版本中添加了_crtBreakAlloc != -1L的判断,所以默认的情况下是不会触发int 3 退出的。

    可以通过调用_CrtSetBreakAlloc设置_crtBreakAlloc的值,当我们设置了新的_crtBreakAlloc,而且_crtBreakAlloc等于_lRequestCurr时就会触发int 3

     

    弄清楚了问题的所在,我们就可以着手解决问题了。VC6dbgheap.c中有两个地方判断了lRequest 是否与_crtBreakAlloc相等,相等后执行指令int 3。我们不用复杂的处理,把int 3替换为nop0x90)指令即可。首先得到“if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc) 对应的二进制指令,用UE打开msvcrtd.dll,使用16进制编辑模式,查找得到的二进制指令,发现确实只有二处,把紧接着它们的0xCC替换为0x90,问题解决。


    最新回复(0)