2014-02-11 53 views
0

我已經閱讀了StackOverflow和CodeProject.net中有關SEH exceptions的許多文章。SEH StackOverflow異常 - 是否真的不可能捕獲?

當我在我的C++程序中實現了SEH exceptions處理後,我受到了堆棧溢出異常的影響,該異常未被我的軟件捕獲。

經過接下來的部分研究,我明白,以編程方式檢測此類異常是不可能的,因爲我們沒有可用的堆棧地址空間,所以程序內存已損壞。

我想問你一些關於處理堆棧溢出異常的經驗。它看起來像一個挑戰,我真的很感興趣,如果這是不可能在非託管代碼編程語言?

下面我介紹了我的示例程序(C++)的一部分,它重現stack overflow exception。它完全適用於任何SEH exception,但沒有堆棧溢出:

LONG WINAPI SehHandler(PEXCEPTION_POINTERS pExceptionPtrs) 
{ 
    cerr << "Handled SEH exception!\n"; 
    cerr << "ContextRecord: " << pExceptionPtrs->ContextRecord << endl; 
    cerr << "ExceptionRecord: " << pExceptionPtrs->ExceptionRecord << endl; 

    // Write minidump file 
    CreateMiniDump(pExceptionPtrs); 

    // Terminate process 
    TerminateProcess(GetCurrentProcess(), 1); 

    return EXCEPTION_EXECUTE_HANDLER; 
} 

int fib(unsigned int n) { 
    if(n == 0) return 0; 
    if(n == 1) return 1; 
    return fib(n-1)+fib(n-2); 
} 

int main(){ 
    SetUnhandledExceptionFilter(SehHandler); 
    cout << fib(1000000); 
    return 0; 
} 
+3

你讀過[此知識庫文章](http://support.microsoft.com/kb/315937)? –

回答

8

是的,你可以得到一個小型轉儲出SO崩潰,但從來沒有你現在正在做的方式。您的SehHandler()函數在觸發異常的線程上運行。而且它處於危險的狀態,你有大約7080字節的應急棧空間可以做你需要做的事情。如果你使用它,那麼程序將會失敗並出現不可捕獲的訪問衝突異常。

您不能調用MiniDumpWriteDump()並希望能夠存活它,該函數需要的堆棧超出您的可用空間。所以這是一個沒有小型轉儲的硬件kaboom。

您需要另一個線程才能進行該調用。例如,這可能是您在初始化時創建的線程,並使用WaitForMultipleObjects()調用進行阻塞。你的SehHandler()可以調用SetEvent()來喚醒它。將PEXCEPTION_POINTERS值寫入全局變量後。並無限制地阻止線程創建minidump並中止進程。

Fwiw,迄今爲止最好的線程是在另一個進程。這也可以讓你處理那些徹底破壞進程狀態的非常令人討厭的進程。從初始化開始的「警衛」過程。使用一個命名事件來發信號通知PEXCEPTION_POINTERS,例如一個內存映射文件。不要在SehHandler()中啓動它,進程堆不再可靠,所以CreateProcess()不能再工作,你必須儘早完成。

+0

您能否澄清一下「〜7080字節的應急堆棧空間剩餘」?它在哪裏以及如何使用它? – n0p

+1

Hans,我明白你爲什麼沒有迴應n0p,因爲很明顯緊急空間在哪裏(在棧上)以及如何使用它(自動變量在異常過濾器和它調用的函數中)。但詢問心靈想知道7080字節的數字來自哪裏...... –

1

Hans Passant的答案表明有7080字節的應急棧。我不知道信息來自哪裏,他沒有回答上面的@nop,我的發現表明這個信息是不正確的。然而這個網站將不會允許我上面出於某種原因發表評論,所以我就離開這個在這裏...

有可以用來查詢和設置多少緊急棧留給堆棧功能處理程序:SetThreadStackGuarantee()。需要注意的是自Windows 10肯定(但我認爲這是因爲Windows 7的爲好),在大多數情況下,這值是0。因此,默認情況下,處理程序中無法執行任何複雜操作。正如Hans所建議的那樣,您可能能夠發出另一個線程或外部過程的信號,但僅此而已。

但是,如果你不希望實現這樣一個複雜的解決方案,並能抽出堆棧上有些鬆懈的空間,這是最容易使用SetThreadStackGuarantee()集這足夠高,你可以繼續處理的值堆棧溢出異常,就像任何其他。請注意,您需要在每個需要此功能的線程上調用此函數,並在發生堆棧溢出之前將其稱爲,因此最好在線程初始化時進行。

+0

公平地說,n0p沒有問漢斯信息來自哪裏。 –

+0

他確實要求澄清,他沒有得到任何。這些信息似乎不正確。我想在那裏發表評論並提出問題,因爲我想知道漢斯從哪裏得到的。然而,這個網站似乎更喜歡滑稽點和獎牌,而不是實際的正確性和信息來源。因此,我無法在那裏發表評論,現在可以標記漢斯或指出他接受的答案需要澄清,更正和引用。 – AlenL

+0

n0p問你如何使用堆棧空間。那麼,他問你如何使用「緊急堆棧空間」,就好像它需要特殊訪問一樣。由於您像使用其他任何堆棧空間一樣使用它,因此這不是一個真正有效的問題。 –