AddressSanitizer

概觀

C 和 C++ 語言功能強大,但可能會遭受影響程式正確性和程式安全性的 Bug 類別。 從 Visual Studio 2019 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

Visual Studio 安裝程式 中的 C++ 工作負載預設會安裝 AddressSanitizer 連結庫和 IDE 整合。 不過,如果您要從舊版Visual Studio 2019升級,請使用安裝程式在升級之後啟用 ASan 支援。 您可以透過 [工具取得工具和>功能],從 Visual Studio 主功能表開啟安裝程式...從 Visual Studio 安裝程式 選擇 [修改],在現有的Visual Studio安裝上取得下列畫面。

Screenshot of the Visual Studio Installer. The C++ AddressSanitizer component, under the Optional section, is highlighted.

注意

如果您在新的更新上執行 Visual Studio,但尚未安裝 ASan,當您執行程式代碼時會收到錯誤:

LNK1356:找不到連結庫 'clang_rt.asan_dynamic-i386.lib'

使用 AddressSanitizer

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

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

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

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

本文涵蓋啟用先前列出的三個工作流程所需的資訊。 此資訊專屬於 AddressSanitizer 的平臺相依 Windows 10 實作。 本檔補充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 /GLPGO)。 此選項適用於靜態和動態CCT(例如、/MD/MDd/MT/MTd)。 不論您是建立 EXE 還是 DLL,它都適用。 針對呼叫堆疊的最佳格式設定,需要偵錯資訊。 在下列範例中, cl /fsanitize=address /Zi 會在命令行上傳遞。

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 的開發人員命令提示字元,使用 編譯main.cpp/fsanitize=address /Zi

Screenshot of a command prompt showing the command to compile with AddressSanitizer options. The command is: `cl main.cpp -faanitize-address /Zi`.

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

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

Screenshot of the debugger showing a basic global overflow error.

錯誤報告中有七個紅色醒目提示可識別關鍵資訊片段。 它們會對應至此螢幕快照後面的編號清單。 編號方塊反白顯示下列文字:1) global-buffer-overflow 2) size 4 3) basic-global-overflow.cpp 7 4) 在 'basic-global-overflow' 中 定義的全域變數 'x' 右邊寫入 大小為 400 6) 大小 400 6 的 cpp:3:8' 5) 00 00 00 00[f9]f9 7) 方塊位於陰影位元組圖例區域,並包含全域紅色區域: f9

紅色醒目提示,從上到下

  1. 記憶體安全性 Bug 是全域緩衝區溢位。
  2. 儲存在任何使用者定義變數外部的 4 個字節 (32 位)。
  3. 存放區是在第 7 行檔案中定義的函main()basic-global-overflow.cpp式中發生。
  4. 名為 x 的變數會在第 3 行的 basic-global-overflow.cpp 中定義,從數據行 8 開始
  5. 這個全域變數 x 的大小為 400 個字節
  6. 描述商店目標地址的確切 陰影位元組 具有 值 0xf9
  7. 陰影位元組圖例說 0xf9 是右邊填補的區域 int x[100]

注意

呼叫堆疊中的函式名稱是透過 運行時間在錯誤時叫用的 LLVM 符號化程式 所產生。

在 Visual Studio 中使用 AddressSanitizer

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

Screenshot of the Property Pages dialog showing the Enable AddressSanitizer property.

若要從 IDE 建置,請退出宣告任何 不相容的選項。 針對使用 /Od (或偵錯模式編譯的現有專案),您可能需要關閉下列選項:

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

Screenshot of the debugger showing a global buffer overflow error.

從 Visual Studio 使用 AddressSanitizer:CMake

若要為建立目標 WindowsCMake 專案啟用 AddressSanitizer,請遵循下列步驟:

  1. 開啟 IDE 頂端工具列中的 [組態] 下拉式清單,然後選取 [管理組態]。

    Screenshot of the CMake configuration dropdown. It displays options like x64 Debug, x64 Release, and so on. At the bottom of the list, Manage Configurations... is highlighted.

    這會開啟 CMake 專案 設定 編輯器,以反映專案CMakeSettings.json檔案的內容。

  2. 選擇編輯器中的 [ 編輯 JSON] 連結。 此選取範圍會將檢視切換為原始 JSON。

  3. 在 中"configurePresets":將下列代碼段新增至"windows-base"預設,以開啟 Address Sanitizer:

    "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. 如果指定 [編輯後繼續],位址清理程式將無法運作。/ZI新 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 組建擷取錯誤。

    Screenshot of an exception that says: Address Sanitizer Error: Global buffer overflow. In the background, address sanitizer output is visible in command window.

AddressSanitizer 損毀傾印

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

分析 Bug 時,這些新的傾印檔案可能會導致效率。 您不需要重新執行,或尋找遠端數據,或尋找離線的電腦。

若要產生新類型的傾印檔案,可在 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 誤判的功能。 該專業領域在考慮具有數十年現有程序代碼的 Interop 時,強制執行了所需的有效測試完整性。 後續版本可能會考慮更多功能:

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

現有的產業檔

AddressSanitizer 技術的這些語言和平臺相依實作已存在廣泛的檔。

AddressSanitizer這篇開創性文件說明實作。

另請參閱

AddressSanitizer 已知問題
AddressSanitizer 組建和語言參考
AddressSanitizer 運行時間參考
AddressSanitizer 陰影位元組
AddressSanitizer 雲端或分散式測試
AddressSanitizer 調試程式整合
AddressSanitizer 錯誤範例