使用msvc2019的AddressSanitizer
總結
這是至今為止我使用過的幾乎最強內存檢測工具,能力大大強於application verifier, MSVC CRT, 速度遠遠快於boundschecker
原創論文 https://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/37752.pdf
msvc工具介紹 https://devblogs.microsoft.com/cppblog/asan-for-windows-x64-and-debug-build-support/ https://devblogs.microsoft.com/cppblog/addresssanitizer-asan-for-windows-with-msvc/
功能
它的原理是使用bitmap記錄每一個字節的狀況,比如能否寫入,在編譯器每次讀取
- stack-use-after-scope
- stack-buffer-overflow
- stack-buffer-underflow
- heap-buffer-overflow (no underflow)
- heap-use-after-free
- calloc-overflow
- dynamic-stack-buffer-overflow (alloca)
- global-overflow (C++ source code)
- new-delete-type-mismatch
- memcpy-param-overlap
- allocation-size-too-big
- invalid-aligned-alloc-alignment
- use-after-poison
- Intra-object-overflow
- Initialization-order-fiasco
- double-free
- alloc-dealloc-mismatch
性能
AddressSanitizer achieves efficiency without sacrific- ing comprehensiveness. Its average slowdown is just 73% yet it accurately detects bugs at the point of occur- rence. It has found over 300 previously unknown bugs in the Chromium browser and many bugs in other software.
AddressSanitizer在不犧牲全面性的情況下實現了效率。 它的平均速度僅慢了73%,但它可以在發生時准確地檢測到錯誤。 它在Chromium瀏覽器中發現了300多個以前未知的錯誤,在其他軟件中也發現了許多錯誤。
AddressSanitizer可以發現越界訪問(用於堆,堆棧和全局對象)和釋放的堆內存的使用,相對較低的成本降低了73%,並且內存使用量增加了3.4倍,使其成為測試廣泛范圍的理想選擇C / C ++應用程序。
原理
AddressSanitizer由兩部分組成:一個指令模塊和一個運行時庫。指令模塊修改代碼以檢查每次內存訪問的影子狀態,並在堆棧和全局對象周圍創建中毒的紅色區域,以檢測上溢和下溢。當前的實現基於LLVM [4]編譯器基礎結構。運行時庫替換malloc,free和相關函數,在分配的堆區域周圍創建有毒的Redzone,延遲釋放的堆區域的重用,並進行錯誤報告
不能做到的事情
- 未初始化內存
- 某些巧妙的溢出。比如溢出到其他變量區域,卻沒有擦花紅色區域
使用
打開檢測,注意打開它和Edit and continue功能沖突,需要關閉后者
測試代碼
void ff(double (*t[])(int)) {
int x;
char a[2];
a[2] = 1;
}
運行報如下錯
nums contains 4 elements.
=================================================================
==14376==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x00b4566ffcf2 at pc 0x7ff7a01238a5 bp 0x00b4566ffcb0 sp 0x00b4566ffcb8
WRITE of size 1 at 0x00b4566ffcf2 thread T0
==14376==WARNING: Failed to use and restart external symbolizer!
#0 0x7ff7a01238a4 in ff G:\temp\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp:12
#1 0x7ff7a0123a86 in main G:\temp\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp:25
#2 0x7ff7a01270c8 in invoke_main D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
#3 0x7ff7a0126fad in __scrt_common_main_seh D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
#4 0x7ff7a0126e6d in __scrt_common_main D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:330
#5 0x7ff7a0127158 in mainCRTStartup D:\agent\_work\9\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:16
#6 0x7fff50ca2773 in BaseThreadInitThunk+0x13 (C:\WINDOWS\System32\KERNEL32.DLL+0x180012773)
#7 0x7fff52d60d50 in RtlUserThreadStart+0x20 (C:\WINDOWS\SYSTEM32\ntdll.dll+0x180070d50)
Address 0x00b4566ffcf2 is located in stack of thread T0 at offset 34 in frame
#0 0x7ff7a01212c6 in ILT+705(_get_startup_file_mode)+0x0 (G:\temp\ConsoleApplication1\x64\Debug\ConsoleApplication1.exe+0x1400012c6)
This frame has 1 object(s):
[32, 34) 'a' <== Memory access at offset 34 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp, SEH and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow G:\temp\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.cpp:12 in ff
Shadow bytes around the buggy address:
0x0245b3bdff40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdff50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdff60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdff70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0245b3bdff90: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[02]f3
0x0245b3bdffa0: f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdffb0: f1 f1 f1 f1 00 00 f2 f2 f2 f2 00 00 f3 f3 f3 f3
0x0245b3bdffc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdffd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0245b3bdffe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==14376==ABORTING
G:\temp\ConsoleApplication1\x64\Debug\ConsoleApplication1.exe (process 14376) exited with code 1.
Press any key to close this window . . .
Bug
使用發現這個功能在64bit的時候軟件退出的時候總會誤報錯誤,32bit沒有問題
用msvc debugger run的時候啟動crash, callstack如下
> ntdll.dll!memset() Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!__asan::PoisonShadow(unsigned __int64,unsigned __int64,unsigned char) Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!__asan::AsanMapUnmapCallback::OnMap(unsigned __int64,unsigned __int64) Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!__asan::Allocator::InitLinkerInitialized(struct __asan::AllocatorOptions const &) Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!__asan::AsanInitInternal() Unknown Non-user code. Symbols loaded.
ucrtbased.dll!_initterm(void(*)() * first, void(*)() * last) Line 22 C++ Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!dllmain_crt_process_attach() Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!dllmain_crt_dispatch() Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!dllmain_dispatch() Unknown Non-user code. Symbols loaded.
clang_rt.asan_dbg_dynamic-x86_64.dll!_DllMainCRTStartup() Unknown Non-user code. Symbols loaded.
verifier.dll!AVrfpStandardDllEntryPointRoutine() Unknown Non-user code. Symbols loaded.
vrfcore.dll!VfCoreStandardDllEntryPointRoutine(void *,unsigned long,struct _CONTEXT *) Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpCallInitRoutine() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpInitializeNode() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpInitializeGraphRecurse() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpInitializeGraphRecurse() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpInitializeProcess() Unknown Non-user code. Symbols loaded.
ntdll.dll!_LdrpInitialize() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrpInitialize() Unknown Non-user code. Symbols loaded.
ntdll.dll!LdrInitializeThunk() Unknown Non-user code. Symbols loaded.