VC調試版本C運行庫內存申請的一個bug(轉)

 

遇到過一個通信方面的軟件,需要長期運行,做壓力測試時,高負荷連續運行一定天數時必定崩潰,而且都是在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,問題解決。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章