棧又溢位了

2021-10-10 05:04:32 字數 3360 閱讀 8913

執行程式後,異常發生了。對於程式崩潰,早就見怪不怪了。重啟程式,附加偵錯程式,再次執行相同的功能,果然中斷到偵錯程式中。有了上次的經驗(沒仔細看錯誤提示導致懵逼了很久,文章在這裡),仔細檢查了錯誤碼,又是0xc00000fd, stackoverflow。在vs2013中按ctrl + alt + c檢視呼叫棧,發現呼叫棧並不深,沒有遞迴呼叫的跡象。仔細看報錯的位置,居然沒有執行到任何**。下圖是我用測試**截的圖:

函式呼叫的時候,會把引數、區域性變數、返回位址等資訊都儲存在棧上,而棧空間預設只有1 mb,如果呼叫棧幀太多,那麼可能會用光這1 mb,從而導致stack overflow

小貼士:這裡的1 mb不太精確,實際可用的棧空間比1 mb小,最後乙個頁面永遠是不可用的。為了描述簡單而且好記就這麼描述了。

呼叫棧並不深,難道就是這幾個棧幀就把棧耗光了?簡單瀏覽當前函式中的區域性變數和引數,很快就找到了幾個值得懷疑的區域性變數。但是通過sizeof檢視對應結構體大小後發現:雖然大(大概400 kb),但是並沒有大到爆棧的程度。繼續觀察,發現了乙個很有意思的現象,這些變數在每個if else分支中都定義了乙份,難道這些分支中的區域性變數占用的棧空間被累加了?乙個大概400 kb3個加起來就超過1 mb了(預設的棧大小是1 mb),足以爆棧了!到底是不是這樣的呢?

為了確認猜想是否正確,新建乙個簡單的測試工程,測試**如下:

#include "stdafx.h"

struct bigdata ;

void use(bigdata* pdata)

void corrpuptstackeasyly(int argc)

else if (argc == 3)

else

}int _tmain(int argc, _tchar* ar**)

bigdata大概占用400 kb,如果猜想(三個bigdata型別的區域性變數會占用1.2 mb左右的空間)是正確的,那麼這個函式應該會爆棧。編譯執行,果真和猜想的一樣——爆棧了!

檢視函式corrpuptstackeasyly對應的反彙編,如下圖:

0x12c0dc轉換成十進位制是1229020大概是1.2 mb__alloca_probe是編譯器生成的函式,內部直接跳轉到_chkstk

從名字可以很容易的猜出_chkstk是用來檢查棧的。當函式中包含超大區域性變數(大於等於乙個頁面,4 kb)時,編譯器會在函式頭部插入一段檢查棧是否夠用的**。

_chkstk雖然是彙編**寫的,但是內部邏輯並不複雜,而且在安裝vs的時候提供了帶注釋的原始碼,可讀性極強。我機器上同時安裝了vs2010vs2013,可以在下圖中的幾個位置找到_chkstk對應的彙編**檔案chkstk.asm,如下圖:

因為這個函式超級精煉,有效彙編**不到20行,這裡截圖放上來,感興趣的小夥伴兒可以讀一讀:

稍微解釋幾個關鍵點:

eax記錄了需要檢查的棧大小,外部呼叫的時候需要設定好。

ecx記錄最低位址(棧是從高向低擴充套件的)。(73,74 行)

根據esp計算出當前位址所屬頁面的起始位置。(83,84 行)

判斷是否結束,沒結束則執行 5,6,7步。(87, 88 行)

讀取四位元組(99行)。

本行**是關鍵,如果訪問的位址所在的頁面是保護頁面(帶有page_guard屬性)並且經判定不需要拋棧溢位異常,則會觸發status_guard_page_violation異常(應該內部叫_xcpt_guard_page_violation,異常碼是0x80000001),作業系統會去除保護頁面的保護屬性,並分配物理記憶體,為下乙個介面設定保護屬性。

跳轉到第四步(cs10的位置),不斷重複這個過程。(100行)

注意:建立執行緒的時候指定了乙個棧保留大小(預設是1mb),剛開始的時候這1mb並不是都對應著物理記憶體,是按需分配的。這裡說的增長棧空間,並不是棧保留大小變大了,而是占用的物理頁增多了。相信大多數小夥伴兒應該已經知道了,但是這裡還是要囉嗦一句:訪問某個虛擬位址的時候,只有當這個虛擬位址對應的頁面有與之對應的物理頁面才可以訪問,否則會報訪問異常。

知道問題的根源後,解決就簡單了。只需要消除重複的大區域性變數即可。把分支中重複的變數提取到函式開始的位置即可。

void corrpuptstackeasyly(int argc)

else if (argc == 3)

else

}

說實話,解決完這個問題後,我是震驚的!vs真的就這麼簡單粗暴的把所有區域性變數的大小累加起來為函式分配棧空間嗎?這太太太不合理了!如果真是這樣,分支多的函式太有可能出現棧溢位了。個人覺得合理的做法是:把分支中占用記憶體最大的作為分支部分的記憶體占用,加上其它不在分支中的區域性變數的記憶體空間來為函式分配棧空間。

棧空間 棧又溢位了

執行程式後,異常發生了。對於程式崩潰,早就見怪不怪了。重啟程式,附加偵錯程式,再次執行相同的功能,果然中斷到偵錯程式中。有了上次的經驗 沒仔細看錯誤提示導致懵逼了很久,文章在這裡 仔細檢查了錯誤碼,又是0xc00000fd,stackoverflow。在vs2013中按ctrl alt c檢視呼叫棧...

棧溢位和棧記憶體溢位

棧記憶體溢位是指使用者棧的大小最多為8 10mb,分配超過棧大小的變數則會導致棧記憶體溢位。如char c 1024102411 11mb 棧溢位指的是程式向棧中某個變數中寫入的位元組數超過了這個變數本身所申請的位元組數,因而導致與其相鄰的棧中的變數的值被改變。如char c 10 memset c...

棧溢位,記憶體溢位

對於一台伺服器而言,每乙個使用者請求,都會產生乙個執行緒來處理這個請求,每乙個執行緒對應著乙個棧,棧會分配記憶體,此時如果請求過多,這時候記憶體不夠了,就會發生棧記憶體溢位。棧溢位是指不斷的呼叫方法,不斷的壓棧,最終超出了棧允許的棧深度,就會發生棧溢位,比如遞迴操作沒有終止,死迴圈。可以把記憶體比作...