LINUX 下使用Address Sanitizer ,以及不能运行的问题
文章目錄
- 一、 簡介
- 二、AddressSanitizer 的使用
-
- 使用方法
- 1.使用添加編譯選項的方式使用ASan
- 2.使用CMake添加編譯選項
- 三、測試
-
-
- 不添加Asan選項,不會有任何輸出
- 添加-fsanitize=address選項:
- 簡單修改程序,釋放申請的內(nèi)存空間:
-
- 四、不能在gdb、lldb等調(diào)試器運(yùn)行帶有AddressSanitizer的程序
- 參考
一、 簡介
所謂工欲善其事,必先利其器。在linux系統(tǒng)下進(jìn)行C++的開發(fā)時,出現(xiàn)內(nèi)存訪問錯誤是非常常見的事情,且大多數(shù)情況下gcc和gdb等編譯器和調(diào)試器都不會提示我們發(fā)生內(nèi)存錯誤,通常會導(dǎo)致嚴(yán)重的后果且難以定位問題。
AddressSanitizer (ASan)是一個性能非常好的 C/C++ 內(nèi)存錯誤探測工具。它由編譯器的插樁模塊(目前,LLVM 通過)和替換了 malloc 函數(shù)的運(yùn)行時庫組成。這個工具可以探測如下這些類型的錯誤:
- 對堆,棧和全局內(nèi)存的訪問越界(堆緩沖區(qū)溢出,棧緩沖區(qū)溢出,和全局緩沖區(qū)溢出)
- UAP(Use-after-free,懸掛指針的解引用,或者說野指針)
- Use-after-return(無效的棧上內(nèi)存,運(yùn)行時標(biāo)記
- ASAN_OPTIONS=detect_stack_use_after_return=1)
- Use-After-Scope(作用域外訪問,clang 標(biāo)記 -fsanitize- address-use-after-scope )
- 內(nèi)存的重復(fù)釋放
- 初始化順序的 bug
- 內(nèi)存泄漏
這個工具非常快。通常情況下,內(nèi)存問題探測這類調(diào)試工具的引入,會導(dǎo)致原有應(yīng)用程序運(yùn)行性能的大幅下降,比如大名鼎鼎的 valgrind 據(jù)說會導(dǎo)致應(yīng)用程序性能下降到正常情況的十幾分之一,但引入 AddressSanitizer 只會減慢運(yùn)行速度的一半。
使用 AddressSanitizer 可以在程序發(fā)生內(nèi)存問題的時候及時檢查出來,精準(zhǔn)定位發(fā)生內(nèi)存問題的位置,大大提高我們debug的效率。
二、AddressSanitizer 的使用
自 LLVM 的版本 3.1 和 GCC 的版本 4.8 開始,AddressSanitizer 就是它們的一部分。因此只要版本正確,無需額外鏈接庫或者編譯源碼,只需編譯的時候加上選項即可使用。
使用方法
1.使用添加編譯選項的方式使用ASan
- 使用
-fsanitize=address選項編譯和鏈接你的程序。
例如:
gcc -fsanitize=address -O1 -g -o main main.cpp
- 如果有其他需要,加上-fno-omit-frame-pointer等其他編譯選項
gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g -o main main.cpp
可選擇-O1或者更高的優(yōu)化級別編譯
2.使用CMake添加編譯選項
在CMakeLists文件中添加如下行
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address")
對于AddressSanitizer還有很多編譯選項,這里不一一列舉,若有需要自行去官方文檔查詢。
三、測試
以下程序是有兩個很明顯的內(nèi)存錯誤:
#include <iostream>
using namespace std;int main() {char *c = new char(10);c[1] = 'a';return 0;
}
不添加Asan選項,不會有任何輸出
ubuntu@VM-24-14-ubuntu:~/CppTest$ g++ -O1 -g -o test test.cpp
ubuntu@VM-24-14-ubuntu:~/CppTest$ ./test
ubuntu@VM-24-14-ubuntu:~/CppTest$
編譯器的緘默不語是導(dǎo)致程序員痛苦整宿的元兇之一。
添加-fsanitize=address選項:
ubuntu@VM-24-14-ubuntu:~/CppTest$ g++ -fsanitize=address -O1 -g -o test test.cpp
ubuntu@VM-24-14-ubuntu:~/CppTest$ ./test
==175312==AddressSanitizer: libc interceptors initialized
|| `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
|| `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
|| `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap ||
|| `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
|| `[0x000000000000, 0x00007fff7fff]` || LowMem ||
MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x02008fff6fff
redzone=16
max_redzone=2048
quarantine_size_mb=256M
thread_local_quarantine_size_kb=1024K
malloc_context_size=30
SHADOW_SCALE: 3
SHADOW_GRANULARITY: 8
SHADOW_OFFSET: 0x7fff8000
==175312==Installed the sigaction for signal 11
==175312==Installed the sigaction for signal 7
==175312==Installed the sigaction for signal 8
==175312==T0: stack [0x7ffc3e50a000,0x7ffc3ed0a000) size 0x800000; local=0x7ffc3ed08294
==175312==AddressSanitizer Init done
==175313==Processing thread 175312.
==175313==Stack at 0x7ffc3e50a000-0x7ffc3ed0a000 (SP = 0x7ffc3ed07e88).
==175313==TLS at 0x7fba89ae14c0-0x7fba89ae2580.=================================================================
==175312==ERROR: LeakSanitizer: detected memory leaksDirect leak of 1 byte(s) in 1 object(s) allocated from:#0 0x7fba8a165587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104#1 0x55840905425a in main /home/ubuntu/CppTest/test.cpp:5SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s).
ubuntu@VM-24-14-ubuntu:~/CppTest$
提示檢測到第五行出現(xiàn)內(nèi)存泄漏問題。原來是源程序new了一個對象,并沒有delete。
簡單修改程序,釋放申請的內(nèi)存空間:
#include <iostream>
using namespace std;int main() {char *c = new char(10);c[1] = 'a';delete c;return 0;
}
再次編譯運(yùn)行:
ubuntu@VM-24-14-ubuntu:~/CppTest$ g++ -fsanitize=address -O1 -g -o test test.cpp
ubuntu@VM-24-14-ubuntu:~/CppTest$ ./test
==175937==AddressSanitizer: libc interceptors initialized
|| `[0x10007fff8000, 0x7fffffffffff]` || HighMem ||
|| `[0x02008fff7000, 0x10007fff7fff]` || HighShadow ||
|| `[0x00008fff7000, 0x02008fff6fff]` || ShadowGap ||
|| `[0x00007fff8000, 0x00008fff6fff]` || LowShadow ||
|| `[0x000000000000, 0x00007fff7fff]` || LowMem ||
MemToShadow(shadow): 0x00008fff7000 0x000091ff6dff 0x004091ff6e00 0x02008fff6fff
redzone=16
max_redzone=2048
quarantine_size_mb=256M
thread_local_quarantine_size_kb=1024K
malloc_context_size=30
SHADOW_SCALE: 3
SHADOW_GRANULARITY: 8
SHADOW_OFFSET: 0x7fff8000
==175937==Installed the sigaction for signal 11
==175937==Installed the sigaction for signal 7
==175937==Installed the sigaction for signal 8
==175937==T0: stack [0x7ffc9bbc7000,0x7ffc9c3c7000) size 0x800000; local=0x7ffc9c3c5014
==175937==AddressSanitizer Init done
=================================================================
==175937==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000011 at pc 0x55ebd66cc2de bp 0x7ffc9c3c4ff0 sp 0x7ffc9c3c4fe0
WRITE of size 1 at 0x602000000011 thread T0#0 0x55ebd66cc2dd in main /home/ubuntu/CppTest/test.cpp:6#1 0x7fa58b097082 in __libc_start_main ../csu/libc-start.c:308#2 0x55ebd66cc1cd in _start (/home/ubuntu/CppTest/test+0x11cd)0x602000000011 is located 0 bytes to the right of 1-byte region [0x602000000010,0x602000000011)
allocated by thread T0 here:#0 0x7fa58b6c0587 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cc:104#1 0x55ebd66cc29a in main /home/ubuntu/CppTest/test.cpp:5SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ubuntu/CppTest/test.cpp:6 in main
Shadow bytes around the buggy address:0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[01]fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: faFreed heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7Container overflow: fcArray cookie: acIntra object redzone: bbASan internal: feLeft alloca redzone: caRight alloca redzone: cbShadow gap: cc
==175937==ABORTING
ubuntu@VM-24-14-ubuntu:~/CppTest$
這次提示又不一樣了,提示第6行出現(xiàn)了heap-buffer-overflow。熟悉錯誤信息的同學(xué)很快就能意識到,這是提示程序出現(xiàn)了越界訪問內(nèi)存的錯誤。
通關(guān)這幾次測試,我們可以發(fā)現(xiàn),AddressSanitizer可以幫助我們快速定位編譯器檢測不出來的內(nèi)存問題,大大提高了我們debug的效率。
四、不能在gdb、lldb等調(diào)試器運(yùn)行帶有AddressSanitizer的程序
筆者就是為了記錄這個問題,特意包了這碗餃子。
當(dāng)嘗試使用gdb調(diào)試添加了AddressSanitizer編譯選項的程序時,程序會拒絕執(zhí)行,提示:
==163135==LeakSanitizer has encountered a fatal error.
==163135==HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1
==163135==HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)
根據(jù)網(wǎng)上找到的資料,LeakSanitizer在內(nèi)部使用ptrace,可能會掛起所有線程,以便它可以掃描泄漏而不會產(chǎn)生誤報。只有一個應(yīng)用程序可以使用ptrace,因此,如果您在gdb或strace下運(yùn)行應(yīng)用程序,則LeakSanitizer將無法通過ptrace進(jìn)行附加。
說人話就是,LINUX系統(tǒng)下只同時允許一個進(jìn)程使用ptrace()這個系統(tǒng)調(diào)用,而ptrace是用于進(jìn)程跟蹤的,它提供了父進(jìn)程可以觀察和控制其子進(jìn)程執(zhí)行的能力,并允許父進(jìn)程檢查和替換子進(jìn)程的內(nèi)核鏡像(包括寄存器)的值。
gdb等調(diào)試器也會使用ptrace,因此與LeakSanitizer相沖突,不允許執(zhí)行進(jìn)程。
參考
Address Sanitizer 用法
Linux 下的 AddressSanitizer
c++ Asan(address-sanitize)的配置和使用
Ptrace 詳解
總結(jié)
以上是生活随笔為你收集整理的LINUX 下使用Address Sanitizer ,以及不能运行的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1153 Decode Registra
- 下一篇: PAT 1152 Google Recr