分享方式:


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

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

Visual Studio 安裝程式的螢幕快照。C++ AddressSanitizer 元件會在 [選用] 區段底下反白顯示。

注意

如果您在新的更新上執行 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

命令提示字元的螢幕快照,其中顯示使用 AddressSanitizer 選項編譯的命令。此命令為:『cl main.cpp -faanitize-address /Zi』。

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

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

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

錯誤報告中有七個紅色醒目提示可識別關鍵資訊片段。 它們會對應至此螢幕快照後面的編號清單。 編號方塊反白顯示下列文字:1) global-buffer-overflow 2) 大小為 4 3) basic-global-overflow.cpp 7 4) 在 'basic-global-overflow.cpp:3:8' 5) 大小為 400 6) 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++>General],然後修改 [啟用 AddressSanitizer] 屬性。 選取 [確定] 儲存您的變更。

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

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

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

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

從 Visual Studio 使用 AddressSanitizer:CMake

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

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

    CMake 設定下拉式清單的螢幕快照。它會顯示 x64 偵錯、x64 版本等選項。在清單底部,管理組態...反白顯示。

    這會開啟 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 組建擷取錯誤。

    顯示例外狀況的螢幕快照:位址清理程序錯誤:全域緩衝區溢位。在背景中,命令視窗中會顯示位址清理器輸出。

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 (external) 的開創性檔描述實作。

另請參閱

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