在AndroidStudio上使用AddressSanitizer


在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編譯器。

  1. 開啟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)
  1. 使用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:

infteK.png

參考

clang: AddressSanitizer
Android Native內存檢測
內存檢測工具
AddressSanitizerOnAndroid


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM