Address Sanitizer

Android NDK mulai mendukung Address Sanitizer sejak API level 27 (Android O MR 1).

ASan adalah alat cepat berbasis compiler untuk mendeteksi bug memori dalam kode native. ASan dapat mendeteksi:

  • Stack dan luapan/underflow buffer heap
  • Penggunaan heap setelah tersedia
  • Penggunaan stack di luar cakupan
  • Double free/wild free

ASan dapat dijalankan pada ARM 32 bit dan 64 bit, serta x86 dan x86-64. Overhead CPU ASan kurang lebih 2x, overhead ukuran kodenya antara 50% hingga 2x, dan overhead memorinya cukup besar (bergantung pada pola alokasi Anda, tetapi dalam urutan 2x).

Untuk ARM 64 bit, HWASan dapat menjadi opsi yang lebih efektif.

Membuat build

Untuk membuat kode native (JNI) aplikasi dengan Address Sanitizer, lakukan hal berikut:

ndk-build

Dalam Application.mk Anda:

APP_STL := c++_shared # Or system, or none.
    APP_CFLAGS := -fsanitize=address -fno-omit-frame-pointer
    APP_LDFLAGS := -fsanitize=address
    

Untuk setiap modul dalam Android.mk:

LOCAL_ARM_MODE := arm
    

CMake

Dalam build.gradle modul Anda:

android {
        defaultConfig {
            externalNativeBuild {
                cmake {
                    # Can also use system or none as ANDROID_STL.
                    arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared"
                }
            }
        }
    }
    

Untuk setiap target dalam CMakeLists.txt:

target_compile_options(${TARGET} PUBLIC -fsanitize=address -fno-omit-frame-pointer)
    set_target_properties(${TARGET} PROPERTIES LINK_FLAGS -fsanitize=address)
    

Eksekusi

Dimulai dari Android O MR1 (API level 27), aplikasi dapat menyediakan skrip wrap.sh yang dapat menggabungkan atau mengganti proses aplikasi. Skrip ini memungkinkan aplikasi yang dapat di-debug menyesuaikan proses memulai aplikasinya, yang memungkinkan penggunaan ASan di perangkat produksi.

  1. Tambahkan android:debuggable ke manifes aplikasi.
  2. Tambahkan library runtime ASan ke jniLibs modul aplikasi Anda.
  3. Tambahkan file wrap.sh dengan konten berikut ini ke setiap direktori yang sama.

    #!/system/bin/sh
        HERE="$(cd "$(dirname "$0")" && pwd)"
        export ASAN_OPTIONS=log_to_syslog=false,allow_user_segv_handler=1
        ASAN_LIB=$(ls $HERE/libclang_rt.asan-*-android.so)
        if [ -f "$HERE/libc++_shared.so" ]; then
            # Workaround for https://github.com/android-ndk/ndk/issues/988.
            export LD_PRELOAD="$ASAN_LIB $HERE/libc++_shared.so"
        else
            export LD_PRELOAD="$ASAN_LIB"
        fi
        "$@"
        

Dengan asumsi bahwa modul aplikasi project diberi nama app, struktur direktori akhir Anda harus menyertakan hal berikut:

<project root>
    └── app
        └── src
            └── main
                ├── jniLibs
                │   ├── arm64-v8a
                │   │   └── libclang_rt.asan-aarch64-android.so
                │   ├── armeabi-v7a
                │   │   └── libclang_rt.asan-arm-android.so
                │   ├── x86
                │   │   └── libclang_rt.asan-i686-android.so
                │   └── x86_64
                │       └── libclang_rt.asan-x86_64-android.so
                └── resources
                    └── lib
                        ├── arm64-v8a
                        │   └── wrap.sh
                        ├── armeabi-v7a
                        │   └── wrap.sh
                        ├── x86
                        │   └── wrap.sh
                        └── x86_64
                            └── wrap.sh
    

Pelacakan tumpukan

Address Sanitizer perlu melepaskan stack pada setiap panggilan malloc/realloc/free. Ada dua opsi di sini:

  1. Unwinder berbasis penunjuk frame yang "cepat". Unwinder inilah yang perlu Anda gunakan jika mengikuti petunjuk di bagian membuat.

  2. Unwinder CFI yang "lambat". Dalam mode ini, ASan menggunakan _Unwind_Backtrace. Unwinder ini hanya memerlukan -funwind-tables, yang biasanya diaktifkan secara default.

Unwinder cepat merupakan default untuk malloc/realloc/free. Unwinder lambat adalah default untuk pelacakan tumpukan fatal. Unwinder lambat dapat diaktifkan untuk semua pelacakan tumpukan dengan menambahkan fast_unwind_on_malloc=0 ke variabel ASAN_OPTIONS dalam wrap.sh.