2016-05-25 120 views
8

在下面顯示的代碼中,我使用了所有記錄的方式來檢測異常並生成診斷。它使用C++ try/catch關鍵字,通過__try/__catch擴展關鍵字捕獲SEH異常,使用Windows的AddVectoredExceptionHandler()和SetUnhandledExceptionFilter()winapi函數來安裝VEH/SEH過濾器。如何報告Windows上的堆棧緩衝區溢出?

在Visual C++ 2003中運行:
/GS:輸出「hello,world!」並以退出代碼0結束。
/GS-:輸出「hello,world!」和退出代碼爲0

終止使用Visual C運行該++ 2013:
/GS:無輸出,終止,退出代碼爲-1073740791
/GS-: 「你好,世界」 輸出並與0

退出終止怎樣產生的診斷與有效/ GS一個VS2013編譯的程序?

#include "stdafx.h" 
#include <Windows.h> 

#define CALL_FIRST 1 
#define CALL_LAST 0 

LONG WINAPI MyVectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo) 
{ 
    UNREFERENCED_PARAMETER(ExceptionInfo); 

    printf("MyVectoredHandler\n"); 
    return EXCEPTION_CONTINUE_SEARCH; 
} 

LONG WINAPI MyUnhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo) 
{ 
    printf("SetUnhandledExceptionFilter\n"); 

    return EXCEPTION_CONTINUE_SEARCH; 
} 

void f() 
{ 
    __try 
    { 
     char p[20] = "hello,world!"; 
     p[24] = '!'; 
     printf("%s\n", p); 
    } 
    __except (EXCEPTION_EXECUTE_HANDLER) 
    { 
     printf("f() exception\n"); 
    } 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    AddVectoredExceptionHandler(CALL_FIRST, MyVectoredHandler); 
    SetUnhandledExceptionFilter(MyUnhandledExceptionFilter); 

    try{ 
     f(); 
    } 
    catch (...){ 
     printf("catched f exception\n"); 
    } 
    return 0; 
} 
+0

您的代碼導致未定義的行爲。 「檢測」的唯一可靠方法是在執行界限外訪問 –

+1

之前檢查邊界,例如,如果您使用檢測的容器類型,將執行什麼操作。例如對字符串使用'std :: string',並用'at'成員函數對其進行索引。 – Hurkyl

+0

堆棧超限很難檢測到。它們只在覆蓋堆棧幀時觸發。嘗試切換運行時異常*數組界限超出*。它可能工作。 – cup

回答

4

,處理棧緩衝區溢出檢測,__report_gsfailure()的CRT函數,假定棧幀腐敗誘導惡意軟件攻擊。傳統上,這種惡意軟件會與fs:[0] SEH異常過濾器(存儲在堆棧幀中)進行混淆,以獲取異常處理程序來觸發惡意軟件負載。將數據轉化爲可執行代碼的方法之一。

因此,CRT功能不能認爲拋出異常是安全的。在VS2013中包含的CRT中不再有這種功能,可以回到〜VS2005。如果操作系統支持它,它將會失效,如果不是,則確保註冊的VEH/SEH異常處理程序也不能看到異常。 Kaboom在沒有診斷功能的情況下會崩潰到桌面,除非您連接了調試器。

/SAFESEH選項打敗了這種惡意軟件攻擊,所以它不像以前那麼嚴重。如果您仍處於代碼遭受堆棧損壞錯誤的階段,並且您的應用程序不夠流行,無法成爲惡意軟件的目標,則可以考慮更換CRT功能。

不要和你的上司談談吧,你永遠要成爲這個親自負責給予了巨大的責任,以你的客戶。歷史很少說明一個程序員發生了什麼事情的故事,這個程序員的代碼讓整個公司在一個月內停業。但肯定沒有什麼好看的。

地方靠近你的main()函數粘貼此代碼:

__declspec(noreturn) extern "C" 
void __cdecl __report_gsfailure() { 
    RaiseException(STATUS_STACK_BUFFER_OVERRUN, EXCEPTION_NONCONTINUABLE, 0, nullptr); 
} 

,並計劃很快再次將其刪除。

+0

鏈接失敗時添加自定義__report_gsfailure:錯誤LNK2005:___report_gsfailure已經在LIBCMT.lib(gs_report.obj)中定義 – liuaifu

+1

嗯,這是測試代碼,驗證它的工作VS2015。不知道中文文本是什麼意思,聞起來像gs_report.obj已經被另一個圖書館拉進來,所以現在有兩個。使用鏈接器的/ VERBOSE選項找出爲什麼使用gs_report.obj。強烈地,*強烈*傾向於建立/ MD而不是/ MT。 –

+0

感謝您的解決方案。根據我的研究結果,msvcrtd還引入了另一個__report_gsfailure,所以這隻適用於發佈版本。 –

3

問題沒有解決方案。

溢出數組會導致標準C++中出現未定義的行爲,因此不保證特定的結果。沒有給出可靠的結果對編譯器來說不是問題 - 這是允許的行爲。

我知道沒有任何實現可以保證任何特定的行爲來響應溢出 - VS當然不會。這並不奇怪,因爲編譯器不需要這麼做(即本質上,未定義行爲的含義)。這種情況的原因在於通常難以可靠地或一致地檢測到這種情況。

這意味着檢測數組溢出的唯一一致方法是檢查數組索引在使用它們訪問數組元素並採取適當的操作之前是否有效(例如,拋出一個可被捕獲的異常,而不是執行不良操作)。缺點是它沒有提供一種簡單或可靠的方法來捕獲任意代碼中的錯誤 - 不需要修改所有代碼來執行所需的檢查。