在AndroidStudio上使用AddressSanitizer
AddressSanitizer是Google主導的一個開源內存問題檢測工具。現在也開始支持Android平台,且受Google推薦來替代之前的Valgrind。目前AddressSanitizer能夠發現如下問題:
- Out-of-bounds accesses to heap, stack and globals
- Use-after-free
- Use-after-return (runtime flag ASAN_OPTIONS=detect_stack_use_after_return=1)
- Use-after-scope (clang flag -fsanitize-address-use-after-scope)
- Double-free, invalid free
- Memory leaks (experimental)
其中,值得一提的是Memory leaks,現在還是experiment,准確的說,現在還只支持Linux平台,並不支持Android。所以,想寫一個內存泄露來檢驗AddressSanitizer是否生效就要注意了,就像本人一樣,其實AddressSanitizer都已經正常運行了,然后一直沒有檢測出來Memory leaks就以為沒有生效,導致浪費了很多時間。
For more information on leak detector in AddressSanitizer, see LeakSanitizer. The leak detection is turned on by default on Linux, and can be enabled using
ASAN_OPTIONS=detect_leaks=1
on OS X; however, it is not yet supported on other platforms.
也許有人會說,Android不也是Linux嘛。這個的話,從它的Supported Platforms:
- Linux i386/x86_64 (tested on Ubuntu 12.04)
- OS X 10.7 - 10.11 (i386/x86_64)
- iOS Simulator
- Android ARM
- NetBSD i386/x86_64
- FreeBSD i386/x86_64 (tested on FreeBSD 11-current)
就可以看出來,Linux和Android是不同的。而且這里還注明了必須ARM,也就是說如果用非ARM的Android設備可能就不支持。個人認為這和AddressSanitizer的實現機制有關。因為它是通過重寫比如malloc之類的函數,構造shadow Memory來實現的檢測。而這些函數可能都是匯編語言寫的,所以不同的CPU硬件就需要不同的實現版本。
AS中的應用
編譯指令
其實很簡單,就兩點,開啟AddressSanitizer標志和使用clang編譯器。
- 開啟AddressSanitizer標志:在CMakeLists.txt中添加如下語句:
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g -fsanitize=address -fno-omit-frame-pointer")
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g -fsanitize=address -fno-omit-frame-pointer")
SET (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address")
SET (CMAKE_ANDROID_ARM_MODE ARM)
- 使用clang編譯:在build.gradle的cmake模塊下面添加:
arguments "-DANDROID_TOOLCHAIN=clang"
在設備中安裝ASAN
要點:
- 設備需要root
- 運行ndk中的asan_device_setup腳本
關於asan_device_setup腳本,在AS自帶的ndk-bundle中,其腳本路徑為ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/lib64/clang/7.0.2/bin/asan_device_setup
,而如果是自己單獨安裝的NDK,那么其路徑為NDKROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/asan_device_setup
。如果腳本執行成功,那么設備就會自動重啟,在本人實踐中,最后部分信息如下:
>> Pushing files to the device
Installing /system/lib/libclang_rt.asan-arm-android.so 644
[100%] /system/lib/libclang_rt.asan-arm-android.so
Installing /system/lib64/libclang_rt.asan-aarch64-android.so 644
[100%] /system/lib64/libclang_rt.asan-aarch64-android.so
Installing /system/bin/app_process32 755 u:object_r:zygote_exec:s0
[100%] /system/bin/app_process32
Installing /system/bin/app_process32.real 755 u:object_r:zygote_exec:s0
[100%] /system/bin/app_process32.real
Installing /system/bin/app_process64 755 u:object_r:zygote_exec:s0
[100%] /system/bin/app_process64
Installing /system/bin/app_process64.real 755 u:object_r:zygote_exec:s0
[100%] /system/bin/app_process64.real
Installing /system/bin/asanwrapper 755
[100%] /system/bin/asanwrapper
Installing /system/bin/asanwrapper64 755
[100%] /system/bin/asanwrapper64
>> Restarting shell (asynchronous)
>> Please wait until the device restarts
測試
有了前面兩步,就可以實驗了,其實還是很簡單的。
使用AS新建一個C++支持的默認Android項目,然后修改其native函數如下:
extern "C" JNIEXPORT jstring
JNICALL
Java_com_example_willhua_asantest_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
int *ptr = (int*)malloc(sizeof(int) * 3);
ptr[4] = 6;
return env->NewStringUTF(hello.c_str());
}
然后在注意Build Variant為debug,點擊啟動就OK啦。然后在log中可以看到檢測到了heap-buffer-overflow:
參考
clang: AddressSanitizer
Android Native內存檢測
內存檢測工具
AddressSanitizerOnAndroid