在Android Studio上使用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 ofollow,noindex" target="_blank">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 onUbuntu 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:
Linux公社的RSS地址: https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2018-09/154273.htm