AddressSanitizer 運行時間
AddressSanitizer 運行時間連結庫會攔截常見的記憶體配置函式和作業,以檢查記憶體存取。 有幾個不同的運行時間連結庫支援編譯程式可能會產生的各種可執行文件類型。 只要您在編譯階段傳遞 /fsanitize=address
選項,編譯程式和連結器就會自動連結適當的運行時間連結庫。 您可以在連結時使用 /NODEFAULTLIB
選項來覆寫預設行為。 如需詳細資訊,請參閱 AddressSanitizer 語言、組建和偵錯參考中的連結一節。
使用 cl /fsanitize=address
編譯時,編譯程式會產生管理及檢查 陰影位元組的指示。 您的程式會使用此檢測來檢查堆疊、堆積或全域範圍中的記憶體存取。 編譯程式也會產生描述堆疊和全域變數的元數據。 此元數據可讓運行時間產生精確的錯誤診斷:原始程式碼中的函式名稱、行和數據行。 結合之後,編譯程式檢查和運行時間連結庫可以在運行時間遇到許多記憶體安全性錯誤類型時精確診斷它們。
從 Visual Studio 17.7 Preview 3 開始,連結至 AddressSanitizer 運行時間的運行時間連結庫清單如下。 如需有關 (以靜態方式連結運行時間) 和 /MD
(動態連結運行時間的 redist) 選項的詳細資訊/MT
,請參閱 /MD、/MT、/LD (使用運行時間連結庫)。
注意
在下表中, {arch}
是 i386
或 x86_64
。
這些連結庫會針對架構名稱使用 Clang 慣例。 MSVC 慣例通常 x86
不是 和 x64
i386
x86_64
,而是參考相同的架構。
CRT 選項 | AddressSanitizer 運行時間連結庫 (.lib) | 位址執行時間二進位檔 (.dll) |
---|---|---|
/MT 或 /MTd |
clang_rt.asan_dynamic-{arch} , clang_rt.asan_static_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
/MD 或 /MDd |
clang_rt.asan_dynamic-{arch} , clang_rt.asan_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
下圖顯示如何連結 /MT
、 /MTd
、 /MD
和 /MDd
編譯程式選項的語言執行時間連結庫:
下圖顯示連結運行時間連結庫的三個案例。 第一個是 /MT 或 /MTd。 My_exe.exe和my_dll.dll都會以自己的靜態連結 VCRuntime、Universal CRT 和 C++ 執行時間複本來顯示。 這些案例會顯示 /MD,其中my_exe.exe和my_dll.dll共用vcruntime140.dll、ucrtbase.dll和msvcp140.dll。 最後一個案例顯示 /MDd,其中my_exe.exe和my_dll.dll共用運行時間的偵錯版本:vcruntime140d.dll、ucrtbased.dll和msvcp140d.dll
下圖顯示 ASan 連結庫如何鏈接各種編譯程式選項:
下圖顯示連結 ASan 執行時間連結庫的四個案例。 這些案例適用於 /MT(靜態連結運行時間)、/MTd(靜態連結偵錯運行時間)、/MD(動態鏈接運行時間的 redist)、/MDd(動態連結運行時間的偵錯重新編輯者)。 在所有情況下,my_exe.exe連結及其關聯my_dll.dll連結到單一實例clang-rt.asan-dynamix-x86_64.dll。
即使靜態連結,ASan 運行時間 DLL 也必須存在於運行時間,與其他 C 運行時間元件不同。
舊版
在 Visual Studio 17.7 Preview 3 之前,靜態連結的 (/MT
或 /MTd
) 組建未使用 DLL 相依性。 相反地,AddressSanitizer 運行時間是以靜態方式連結至使用者的 EXE。 DLL 項目接著會從使用者的 EXE 載入匯出,以存取 ASan 功能。
動態連結的專案 (/MD
或 /MDd
) 會根據專案是否已設定偵錯或發行,使用不同的連結庫和 DLL。 如需這些變更及其動機的詳細資訊,請參閱 MSVC 位址清理器 – 所有運行時間組態的一個 DLL。
下表描述 Visual Studio 17.7 Preview 3 之前 AddressSanitizer 運行時間連結庫連結的先前行為:
CRT 選項 | DLL 或 EXE | 調試? | ASan 圖書館 (.lib ) |
ASan 執行時間二進位檔 (.dll ) |
---|---|---|---|---|
/MT |
EXE | No | clang_rt.asan-{arch} , clang_rt.asan_cxx-{arch} |
無 |
/MT |
DLL | No | clang_rt.asan_dll_thunk-{arch} |
無 |
/MD |
或 | No | clang_rt.asan_dynamic-{arch} , clang_rt.asan_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dynamic-{arch} |
/MT |
EXE | Yes | clang_rt.asan_dbg-{arch} , clang_rt.asan_dbg_cxx-{arch} |
無 |
/MT |
DLL | Yes | clang_rt.asan_dbg_dll_thunk-{arch} |
無 |
/MD |
或 | Yes | clang_rt.asan_dbg_dynamic-{arch} , clang_rt.asan_dbg_dynamic_runtime_thunk-{arch} |
clang_rt.asan_dbg_dynamic-{arch} |
下圖顯示 ASan 連結庫如何連結至 Visual Studio 2022 17.7 Preview 3 之前的各種編譯程式選項:
下圖顯示連結 ASan 執行時間連結庫的四個案例。 這些案例適用於 /MT(靜態連結運行時間)、/MTd(靜態連結偵錯運行時間)、/MD(動態鏈接運行時間的 redist)、/MDd(動態連結運行時間的偵錯重新編輯者)。 針對 /MT,my_exe.exe具有 ASan 運行時間的靜態連結複本。 my_dll.dll my_exe.exe 中 ASan 執行時間的連結。 針對 /MTd,圖表相同,不同之處在於它會使用以靜態方式連結的 ASan 運行時間進行偵錯。 針對 /MD,my_exe.exe和my_dll.dll連結至名為 clang_rt.asan_dynamic-x86_64.dll 的動態連結 ASan 運行時間。 針對 /MDd,除了my_exe.exe和my_dll.dll連結至名為 clang_rt.asan_dbg_dynamic-x86_64.dll 的偵錯 ASan 運行時間之外,圖表相同。
函式攔截
AddressSanitizer 可透過許多熱修補技術來達成函式攔截。 這些技術 最好記錄在原始程式碼本身內。
運行時間連結庫會攔截許多常見的記憶體管理和記憶體操作函式。 如需清單,請參閱 已攔截函式的 AddressSanitizer 清單。 配置攔截器會管理與每個配置呼叫相關的元數據和陰影位元組。 每次呼叫 或 delete
之類的 malloc
CRT函式時,攔截器都會在AddressSanitizer陰影記憶體區域中設定特定值,以指出這些堆積位置目前是否可存取,以及配置界限為何。 這些陰影位元組允許編譯程式產生的陰影位元組檢查,以判斷載入或存放區是否有效。
攔截不保證會成功。 如果函式序言太短而無法 jmp
寫入,攔截可能會失敗。 如果發生攔截失敗,程式會 debugbreak
擲回 並停止。 如果您附加調試程式,它會清除攔截問題的原因。 如果您發生此問題, 請回報錯誤。
注意
用戶可以藉由將環境變數 ASAN_WIN_CONTINUE_ON_INTERCEPTION_FAILURE
設定為任何值,選擇性地嘗試繼續超過失敗的攔截。 持續超過攔截失敗可能會導致該函式的錯誤報告遺失。
自訂配置器和 AddressSanitizer 運行時間
AddressSanitizer 運行時間會為一般配置器介面 、malloc
/free
、、(/new
/HeapAlloc
delete
HeapFree
透過 RtlAllocateHeap
/RtlFreeHeap
) 提供攔截器。 許多程式會基於一或多個原因使用自定義配置器,例如任何使用 dlmalloc
的程式,或使用介面和VirtualAlloc()
的解決方案std::allocator
。 編譯程式無法自動將陰影記憶體管理呼叫新增至自定義配置器。 用戶有責任使用 提供的手動中毒介面。 此 API 可讓這些配置器與現有的 AddressSanitizer 運行時間和 陰影位元組 慣例正常運作。
手動 AddressSanitizer 有害介面
啟蒙的介面很簡單,但它會對使用者施加對齊限制。 用戶可藉由匯入 來匯入 sanitizer/asan_interface.h
這些原型。 以下是介面函式原型:
void __asan_poison_memory_region(void const volatile *addr, size_t size);
void __asan_unpoison_memory_region(void const volatile *addr, size_t size);
為了方便起見, AddressSanitizer 介面頭檔 會提供包裝函式宏。 這些宏會檢查 AddressSanitizer 功能是否在編譯期間啟用。 它們可讓您的原始程式碼在不需要時省略有害函式呼叫。 這些宏應該優先於直接呼叫上述函式:
#define ASAN_POISON_MEMORY_REGION(addr, size)
#define ASAN_UNPOISON_MEMORY_REGION(addr, size)
AddressSanitizer 中毒的對齊需求
陰影位元組的任何手動中毒都必須考慮對齊需求。 如有必要,用戶必須新增填補,讓陰影位元組在陰影記憶體中的位元組界限上結束。 AddressSanitizer 陰影記憶體中的每個位都會編碼應用程式記憶體中單一位元組的狀態。 此編碼表示每個配置的總大小,包括任何填補,都必須對齊 8 位元組界限。 如果不符合對齊需求,可能會導致錯誤報告不正確。 不正確的報告可能會顯示為遺漏報告(誤判)或報告非錯誤(誤判)。
如需對齊需求和潛在問題的圖例,請參閱提供的 ASan 對齊範例。 一個是一個小程式,以顯示手動陰影記憶體中毒可能出了什麼問題。 第二個是使用 std::allocator
介面手動中毒的範例實作。
運行時間選項
Microsoft C/C++ (MSVC) 會根據 llvm-project 存放庫中的 Clang AddressSanitizer 運行時間使用運行時間。 因此,大部分的運行時間選項會在兩個版本之間共用。 您可以在這裏取得公用 Clang 執行時間選項的完整清單。 我們會在後續各節中記錄一些差異。 如果您發現無法如預期運作的選項, 請回報錯誤。
不支援的 AddressSanitizer 選項
- detect_container_overflow
- unmap_shadow_on_exit
注意
AddressSanitizer 運行時間選項 halt_on_error
無法以您預期的方式運作。 在 Clang 和 MSVC 執行時間連結庫中,許多錯誤類型都會被視為 不可持續性,包括大部分的記憶體損毀錯誤。
如需詳細資訊,請參閱 Clang 12.0 的差異一節。
MSVC 特定的 AddressSanitizer 運行時間選項
windows_hook_legacy_allocators
布爾值,設定為false
以停用 和LocalAlloc
配置器的攔截GlobalAlloc
。注意
撰寫本文時,公用 llvm-project 運行時間中無法使用此選項
windows_hook_legacy_allocators
。 選項最終可能會貢獻回公用專案;不過,它相依於程式代碼檢閱和社群接受。選項
windows_hook_rtl_allocators
,先前是 AddressSanitizer 實驗性時選擇加入的功能,現在預設會啟用。 在 Visual Studio 2022 17.4.6 版之前的版本,預設值為false
。 在 Visual Studio 2022 17.4.6 版和更新版本中,選項windows_hook_rtl_allocators
預設為true
。iat_overwrite
字串,預設會設定為"error"
。 其他可能的值為"protect"
和"ignore"
。 某些模組可能會覆寫import address table
其他模組的 ,以自定義特定函式的實作。 例如,驅動程式通常會針對特定硬體提供自定義實作。 選項iat_overwrite
會管理 AddressSanitizer 運行時間的保護,以防止特定memoryapi.h
函式的覆寫。 運行時間目前會VirtualAlloc
追蹤、VirtualProtect
和函VirtualQuery
式以進行保護。 此選項適用於 Visual Studio 2022 17.5 版 Preview 1 和更新版本。 下列iat_overwrite
值會控制當覆寫受保護函式時運行時間的反應:- 如果設定為
"error"
(預設值),運行時間會在偵測到覆寫時報告錯誤。 - 如果設定為
"protect"
,運行時間會嘗試避免使用覆寫的定義並繼續。 實際上,函式的原始memoryapi
定義會從運行時間內部使用,以避免無限遞歸。 進程中的其他模組仍會使用覆寫的定義。 - 如果設定為
"ignore"
,運行時間不會嘗試更正任何覆寫的函式,並繼續執行。
- 如果設定為
windows_fast_fail_on_error
布爾值 (false 預設),設定true
為 ,以在列印錯誤報告之後,讓進程以 __fastfail(71) 終止。
注意
當abort_on_error值設定為 true 時,在 Windows 上,程式會以 exit(3) 終止。 為了不變更目前的行為,我們決定改為引進這個新選項。 如果abort_on_error和windows_fast_fail_on_error都成立,程式將會結束__fastfail。
已攔截函式的 AddressSanitizer 列表 (Windows)
AddressSanitizer 運行時間熱模式會啟動許多函式,以在運行時間啟用記憶體安全性檢查。 以下是 AddressSanitizer 執行時間所監視之函式的非完整清單。
默認攔截器
__C_specific_handler
(僅限 x64)_aligned_free
_aligned_malloc
_aligned_msize
_aligned_realloc
_calloc_base
_calloc_crt
_calloc_dbg
(僅限偵錯運行時間)_except_handler3
(僅限 x86)_except_handler4
(僅限 x86) ( 未記載 )_expand
_expand_base
( 未記載 )_expand_dbg
(僅限偵錯運行時間)_free_base
( 未記載 )_free_dbg
(僅限偵錯運行時間)_malloc_base
( 未記載 )_malloc_crt
( 未記載 )_malloc_dbg
(僅限偵錯運行時間)_msize
_msize_base
( 未記載 )_msize_dbg
(僅限偵錯運行時間)_realloc_base
( 未記載 )_realloc_crt
( 未記載 )_realloc_dbg
(僅限偵錯運行時間)_recalloc
_recalloc_base
( 未記載 )_recalloc_crt
( 未記載 )_recalloc_dbg
(僅限偵錯運行時間)_strdup
atoi
atol
calloc
CreateThread
free
frexp
longjmp
malloc
memchr
memcmp
memcpy
memmove
memset
RaiseException
realloc
RtlAllocateHeap
RtlCreateHeap
RtlDestroyHeap
RtlFreeHeap
RtlRaiseException
RtlReAllocateHeap
( 未記載 )RtlSizeHeap
( 未記載 )SetUnhandledExceptionFilter
strcat
strchr
strcmp
strcpy
strcspn
strdup
strlen
strncat
strncmp
strncpy
strnlen
strpbrk
strspn
strstr
strtok
strtol
wcslen
wcsnlen
選擇性攔截器
此處所列的攔截器只會在啟用 AddressSanitizer 運行時間選項時安裝。 設定 windows_hook_legacy_allocators
為 以 false
停用舊版配置器攔截。
set ASAN_OPTIONS=windows_hook_legacy_allocators=false
GlobalAlloc
GlobalFree
GlobalHandle
GlobalLock
GlobalReAlloc
GlobalSize
GlobalUnlock
LocalAlloc
LocalFree
LocalHandle
LocalLock
LocalReAlloc
LocalSize
LocalUnlock
另請參閱
AddressSanitizer 概觀
AddressSanitizer 已知問題
AddressSanitizer 組建和語言參考
AddressSanitizer 陰影位元組
AddressSanitizer 雲端或分散式測試
AddressSanitizer 調試程式整合
AddressSanitizer 錯誤範例