AddressSanitizer

概觀

C 和 C++ 語言功能強大,但也可能存在一類影響程式正確性與安全性的錯誤。 自 2019 Visual Studio 16.9 版本起,Microsoft C/C++ 編譯器(MSVC)與 IDE 支援 AddressSanitizer 消毒器。 AddressSanitizer(ASan)是編譯器和執行期技術,可揭露許多難以尋找的 Bug,且只有誤判。

使用 AddressSanitizer 減少花費的時間:

  • 基本正確性
  • 跨平臺可移植性
  • 安全性
  • 壓力測試
  • 整合新的程序代碼

AddressSanitizer 最初 由 Google 引進,提供在執行期間尋找錯誤的技術,可直接使用您現有的組建系統和現有的測試資產。

AddressSanitizer 會與 Visual Studio 項目系統、CMake 建置系統和 IDE 整合。 專案可以藉由設定項目屬性,或使用一個額外的編譯程式選項來啟用 AddressSanitizer: /fsanitize=address。 新的選項與 x86 和 x64 的所有優化和組態層級相容。 不過,它與編輯並繼續增量鏈接不相容,/RTC

從 Visual Studio 2019 16.9 版開始,Microsoft的 AddressSanitizer 技術可讓您與 Visual Studio IDE 整合。 當清理器在運行時間發現 Bug 時,此功能可以選擇性地建立損毀傾印檔案。 如果您在執行程式之前設定 ASAN_SAVE_DUMPS=MyFileName.dmp 環境變數,則會建立附加額外元數據的當機傾印檔案,以更有效率地進行精確診斷 Bug 的後期調試。 這些傾印檔案可讓您更輕鬆地使用 AddressSanitizer:

  • 本機計算機測試
  • 本地分散式測試
  • 用於測試的雲端式工作流程

安裝 AddressSanitizer

C++ 工作負載在 Visual Studio 安裝程式中預設會安裝 AddressSanitizer 庫和 IDE 整合。 不過,如果您要從舊版Visual Studio 2019升級,請使用安裝程式在升級之後啟用 ASan 支援。 你可以從Visual Studio主選單選擇 Tools>Get Tools and Features 開啟安裝程式。 在 Visual Studio 安裝程式中,於您現有的 Visual Studio 安裝選擇 修改,即可進入下列畫面。

截圖,顯示包含 C++ AddressSanitizer 元件的 Visual Studio 安裝程式,在可選區塊中被高亮標示。

注意

如果你在新更新時執行 Visual Studio,但還沒安裝 ASan,執行程式碼時會收到錯誤訊息:

LNK1356:無法找到函式庫 'clang_rt.asan_dynamic-i386.lib'

使用 AddressSanitizer

使用下列任何常見的開發方法,開始使用 /fsanitize=address 編譯程式選項來建置可執行檔:

  • 命令行組建
  • Visual Studio 專案系統
  • Visual Studio CMake 整合

重新編譯,然後正常執行程式。 此程式碼生成會揭露許多精確辨識的漏洞類型。 這些錯誤會以三種方式回報:在調試程式 IDE 中、命令行上,或儲存在新類型的傾印檔案,以進行精確的離線處理。

Microsoft建議您在這三個標準工作流程中使用 AddressSanitizer:

本文涵蓋啟用先前列出的三個工作流程所需的資訊。 此資訊專屬於 Windows 10(及更新版本)上的 AddressSanitizer 依賴平台 實作。 本文件補充已發布的 Google、Apple 和 GCC 的優秀文檔。

注意

支援僅限於 Windows 10 和更新版本上的 x86 和 x64。 請傳送意見反應 給我們,以瞭解您想要在未來版本中看到的內容。 您的意見反應可協助我們排定未來其他清理程式的優先順序,例如 /fsanitize=thread/fsanitize=leak/fsanitize=memory/fsanitize=undefined/fsanitize=hwaddress。 如果您遇到問題,您可以 在這裡 回報 Bug。

從開發人員命令提示字元使用 AddressSanitizer

/fsanitize=address 中使用 編譯器選項,以啟用 AddressSanitizer 執行時的編譯。 此 /fsanitize=address 選項與現有的 C++ 或 C 優化層級相容,例如 /Od/O1/O2/O2 /GL和 。 此選項適用於靜態和動態CCT(例如、/MD/MDd/MT/MTd)。 不論您是建立 EXE 還是 DLL,它都適用。 需有偵錯資訊以達到呼叫堆疊的最佳格式。 在下列範例中, cl /fsanitize=address /Zi 會在命令行上傳遞。

注意

AddressSanitizer 不支援數據引導優化(PGO)。 AddressSanitizer 不應該用於生產環境。

AddressSanitizer 函式庫 (.lib 檔案)會自動為您連結。 如需詳細資訊,請參閱 AddressSanitizer 語言、組建和偵錯參考

範例 - 基本全域緩衝區溢位

// basic-global-overflow.cpp
#include <stdio.h>
int x[100];
int main() {
    printf("Hello!\n");
    x[100] = 5; // Boom!
    return 0;
}

使用適用於 Visual Studio 2019 的開發人員命令提示字元,並使用 /fsanitize=address /Zi 編譯 basic-global-overflow.cpp

截圖顯示一個命令提示字元,指令是「cl basic-global-overflow.cpp /fsanitize=address /Zi」。

當您在命令行執行產生的 basic-global-overflow.exe 時,它會建立下列格式化的錯誤報告。

請考慮重繪的紅色方塊,以醒目提示七個主要資訊:

除錯器顯示基本全域溢位錯誤的截圖。

紅色標註,從上到下

  1. 記憶體安全性漏洞是全域緩衝區溢位。
  2. 儲存在任何使用者定義變數外部的 4 個字節 (32 位)。
  3. 儲存操作發生在第 7 行的檔案main()中定義的函數basic-global-overflow.cpp中。
  4. 名為 x 的變數是在 basic-global-overflow.cpp 中定義,從第 3 行第 8 欄開始。
  5. 這個全域變數 x 大小為 400 位元組。
  6. 描述儲存目標位址的 精確影子位元組 值為 0xf9
  7. 陰影位元組圖例表示,0xf9 是位於 int x[100] 右側的填充區域。

注意

當 AddressSanitizer 回報錯誤時,ASan 執行時會呼叫 LLVM 符號化 器,產生呼叫堆疊中的函式名稱。

在 Visual Studio 中使用 AddressSanitizer

AddressSanitizer 已與 Visual Studio IDE 整合。 若要開啟 MSBuild 專案的 AddressSanitizer,請以滑鼠右鍵按兩下 方案總管 中的項目,然後選擇 [屬性]。 在 [屬性頁] 對話框中,選取 [組態屬性>C/C++>General],然後修改 [啟用 AddressSanitizer] 屬性。 選取 [確定] 儲存您的變更。

「屬性頁面對話框」的螢幕快照,其中顯示 「啟用 AddressSanitizer」屬性。

若要從 IDE 建置,請選擇不使用任何 不相容的選項。 對於使用(或除錯模式)編譯的 /Od 現有專案,您可能需要關閉以下選項:

若要建置並執行調試程式,請按 F5。 在 Visual Studio 中會出現 '擲回例外' 視窗。

調試程式的螢幕快照,其中顯示全域緩衝區溢位錯誤。

在 Visual Studio 中使用 AddressSanitizer:CMake

若要為針對 Windows 的 CMake 專案啟用 AddressSanitizer,請遵循下列步驟:

  1. 在 IDE 頂端工具列開啟「 設定」 下拉選單,選擇 「管理設定」

    截圖顯示 CMake 設定下拉選單,顯示 x64 除錯、x64 發佈,以及列表底部的「管理配置......」等選項。被標示出來。

    這個動作會開啟 CMake Project設定編輯器,裡面會反映你project CMakeSettings.json 檔案的內容。

  2. 選擇 編輯 JSON 連結。 此選項會將檢視切換為原始 JSON。

  3. 要開啟 AddressSanitizer,請在 "windows-base" 預設 "configurePresets":中加入以下片段。

    "environment": {
      "CFLAGS": "/fsanitize=address",
      "CXXFLAGS": "/fsanitize=address"
    }
    

    之後,"configurePresets" 看起來會像這樣:

        "configurePresets": [
          {
            "name": "windows-base",
            "hidden": true,
            "generator": "Ninja",
            "binaryDir": "${sourceDir}/out/build/${presetName}",
            "installDir": "${sourceDir}/out/install/${presetName}",
            "cacheVariables": {
              "CMAKE_C_COMPILER": "cl.exe",
              "CMAKE_CXX_COMPILER": "cl.exe"
            },
            "condition": {
              "type": "equals",
              "lhs": "${hostSystemName}",
              "rhs": "Windows"
            },
            "environment": {
              "CFLAGS": "/fsanitize=address",
              "CXXFLAGS": "/fsanitize=address"
            }
          },
    
  4. 如果指定了 edit-and-continue ,則 AddressSanitizer 無法運作,預設情況下,新的 CMake 專案會啟用這個功能。 在 `CMakeLists.txt` 中,在開頭為 `#` 的行前面加上 `set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"`,將該行註解掉。 那條線大致是這樣的:

    # set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
    
  5. 使用 Ctrl+S 儲存這個 JSON 檔案。

  6. 從 Visual Studio 選單選擇來清除 CMake 快取目錄,然後重新設定:專案>刪除快取和重新設定。 當提示出現清除快取目錄並重新設定時,請選擇 [是 ]。

  7. 例如,將原始檔案 CMakeProject1.cpp的內容替換為以下程式碼:

    // CMakeProject1.cpp : Defines the entry point for the application
    
    #include <stdio.h>
    
    int x[100];
    
    int main()
    {
        printf("Hello!\n");
        x[100] = 5; // Boom!
        return 0;
    }
    
  8. 選擇 F5 以重新編譯並在調試程式下執行。

    此螢幕快照捕捉了 CMake 建置過程中的錯誤。

    截圖顯示一個例外,內容為:位址淨化錯誤:全域緩衝區溢位。

AddressSanitizer 崩潰報告

我們在 AddressSanitizer 中引進了新功能,以搭配雲端和分散式工作流程使用。 此功能允許在 IDE 中離線檢視 AddressSanitizer 錯誤。 錯誤會疊加在您的來源之上,就像您在即時偵錯中遇到的一樣。

這些新的傾印檔案有助於提升分析錯誤的效率。 您不需要重新執行,或尋找遠端數據,或尋找離線的電腦。

若要產生新類型的傾印檔案,可在 Visual Studio 中於稍後於另一部電腦上檢視:

set ASAN_SAVE_DUMPS=MyFileName.dmp

從 Visual Studio 16.9 開始,你可以在原始碼上方顯示 精確診斷錯誤,這些錯誤儲存在你的 *.dmp 檔案中。

這項新的當機傾印功能 能夠啟用雲端工作流程或分散式測試。 它也可以用來在任何情境中提交詳細且可執行的錯誤報告。

範例錯誤

AddressSanitizer 可以偵測數種記憶體誤用錯誤。 以下是當您使用 AddressSanitizer (/fsanitize=address) 編譯程式選項編譯的二進位檔時所回報的許多運行時錯誤:

如需範例的詳細資訊,請參閱 AddressSanitizer 錯誤範例

Clang 12.0 的差異

MSVC 目前在兩個功能區域中與 Clang 12.0 不同:

  • stack-use-after-scope:此設定預設開啟,無法關閉。
  • stack-use-after-return:此功能需要額外的編譯器選項,僅設定 ASAN_OPTIONS 並無法使用。

這些決策是為了減少傳遞此第一個版本所需的測試矩陣。

未包含可能導致 Visual Studio 2019 16.9 誤判的功能。 該規範在考慮與長達數十年的現有程式碼互操作性時, 強化了需要的有效測試完整性。 未來版本可能會考慮更多功能:

如需詳細資訊,請參閱 使用 MSVC 建置 AddressSanitizer。

現有的產業文件資料

AddressSanitizer 技術的這些語言和平臺相依實作已有廣泛的文件。

這篇關於 AddressSanitizer(external)的開創性論文描述了其實作。

另請參閱