AddressSanitizer

AddressSanitizer (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).

Android 10 dan cabang rilis terbaru AOSP di AArch64 mendukung Hardware-assisted AddressSanitizer (HWASan), alat serupa dengan overhead RAM yang lebih rendah dan rentang bug yang terdeteksi lebih besar. HWASan mendeteksi penggunaan stack setelah ditampilkan, selain bug yang dideteksi oleh ASan.

HWASan memiliki overhead CPU dan ukuran kode yang serupa, tetapi overhead RAM yang jauh lebih kecil (15%). HWASan bersifat non-deterministik. Hanya ada 256 kemungkinan nilai tag, sehingga ada probabilitas tetap sebesar 0,4% untuk tidak menemukan bug. HWASan tidak memiliki zona merah berukuran terbatas seperti ASan untuk mendeteksi overflow dan karantina berkapasitas terbatas untuk mendeteksi penggunaan setelah pelepasan, sehingga tidak masalah bagi HWASan seberapa besar overflow atau seberapa lama memori dilepaskan. Hal ini membuat HWASan lebih baik daripada ASan. Anda dapat membaca selengkapnya tentang desain HWASan atau tentang penggunaan HWASan di Android.

ASan mendeteksi luapan stack/global selain luapan heap, dan berjalan cepat dengan overhead memori minimal.

Dokumen ini menjelaskan cara membuat dan menjalankan sebagian/seluruh Android dengan ASan. Jika Anda membuat aplikasi SDK/NDK dengan ASan, lihat Address Sanitizer sebagai gantinya.

Membersihkan setiap file yang dapat dieksekusi dengan ASan

Tambahkan LOCAL_SANITIZE:=address atau sanitize: { address: true } ke aturan build untuk file yang dapat dieksekusi. Anda dapat menelusuri kode untuk menemukan contoh yang ada atau untuk menemukan sanitizer lain yang tersedia.

Saat bug terdeteksi, ASan mencetak laporan verbose ke output standar dan ke logcat, lalu menyebabkan error pada proses.

Membersihkan library bersama dengan ASan

Karena cara kerja ASan, library yang dibuat dengan ASan hanya dapat digunakan oleh dapat dieksekusi yang dibuat dengan ASan.

Untuk membersihkan library bersama yang digunakan di beberapa executable, yang tidak semuanya dibuat dengan ASan, Anda memerlukan dua salinan library. Cara yang direkomendasikan untuk melakukannya adalah dengan menambahkan kode berikut ke Android.mk untuk modul yang dimaksud:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

Hal ini menempatkan library di /system/lib/asan, bukan /system/lib. Kemudian, jalankan file yang dapat dieksekusi dengan:

LD_LIBRARY_PATH=/system/lib/asan

Untuk daemon sistem, tambahkan kode berikut ke bagian yang sesuai di /init.rc atau /init.$device$.rc.

setenv LD_LIBRARY_PATH /system/lib/asan

Pastikan proses menggunakan library dari /system/lib/asan jika ada dengan membaca /proc/$PID/maps. Jika tidak, Anda mungkin perlu menonaktifkan SELinux:

adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID
# if it is a system service, or may be adb shell stop; adb shell start.

Stack trace yang lebih baik

ASan menggunakan unwinder berbasis frame pointer yang cepat untuk merekam stack trace untuk setiap peristiwa alokasi dan dealokasi memori dalam program. Sebagian besar Android dibuat tanpa pointer frame. Akibatnya, Anda sering kali hanya mendapatkan satu atau dua frame yang bermakna. Untuk memperbaikinya, bangun ulang library dengan ASan (direkomendasikan!), atau dengan:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

Atau, tetapkan ASAN_OPTIONS=fast_unwind_on_malloc=0 di lingkungan proses. Yang terakhir dapat sangat intensif CPU, bergantung pada beban.

Simbolisasi

Awalnya, laporan ASan berisi referensi ke offset dalam biner dan library bersama. Ada dua cara untuk mendapatkan informasi file sumber dan baris:

  • Pastikan biner llvm-symbolizer ada di /system/bin. llvm-symbolizer dibangun dari sumber di third_party/llvm/tools/llvm-symbolizer.
  • Memfilter laporan melalui skrip external/compiler-rt/lib/asan/scripts/symbolize.py script.

Pendekatan kedua dapat memberikan lebih banyak data (yaitu, lokasi file:line) karena ketersediaan library yang disimbolkan di host.

ASan dalam aplikasi

ASan tidak dapat melihat kode Java, tetapi dapat mendeteksi bug di library JNI. Untuk itu, Anda perlu membuat file yang dapat dieksekusi dengan ASan, yang dalam hal ini adalah /system/bin/app_process(32|64). Hal ini memungkinkan ASan di semua aplikasi di perangkat secara bersamaan, yang merupakan beban berat, tetapi perangkat dengan RAM 2 GB seharusnya dapat menanganinya.

Tambahkan LOCAL_SANITIZE:=address ke aturan build app_process di frameworks/base/cmds/app_process.

Edit bagian service zygote dari file system/core/rootdir/init.zygote(32|64).rc yang sesuai untuk menambahkan baris berikut ke blok baris yang diindentasi yang berisi class main, juga diindentasi dengan jumlah yang sama:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

Build, sinkronisasi adb, flash boot fastboot, dan mulai ulang.

Menggunakan properti wrap

Pendekatan di bagian sebelumnya menempatkan ASan ke dalam setiap aplikasi dalam sistem (sebenarnya, ke dalam setiap proses turunan Zygote). Anda dapat menjalankan hanya satu (atau beberapa) aplikasi dengan ASan, dengan mengorbankan beberapa overhead memori untuk peluncuran aplikasi yang lebih lambat.

Hal ini dapat dilakukan dengan memulai aplikasi Anda dengan properti wrap.. Contoh berikut menjalankan aplikasi Gmail di bawah ASan:

adb root
adb shell setenforce 0  # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"

Dalam konteks ini, asanwrapper menulis ulang /system/bin/app_process menjadi /system/bin/asan/app_process, yang dibuat dengan ASan. Selain itu, /system/lib/asan juga ditambahkan di awal jalur penelusuran library dinamis. Dengan cara ini, library yang diinstrumentasi ASan dari /system/lib/asan lebih diutamakan daripada library normal di /system/lib saat berjalan dengan asanwrapper.

Jika bug ditemukan, aplikasi akan mengalami error, dan laporan akan dicetak ke log.

SANITIZE_TARGET

Android 7.0 dan yang lebih tinggi mencakup dukungan untuk membangun seluruh platform Android dengan ASan sekaligus. (Jika Anda membuat rilis yang lebih tinggi dari Android 9, HWASan adalah pilihan yang lebih baik.)

Jalankan perintah berikut di pohon build yang sama.

make -j42
SANITIZE_TARGET=address make -j42

Dalam mode ini, userdata.img berisi library tambahan dan juga harus di-flash ke perangkat. Gunakan command line berikut:

fastboot flash userdata && fastboot flashall

Tindakan ini akan mem-build dua set library bersama: normal di /system/lib (pemanggilan make pertama), dan yang diinstrumentasi ASan di /data/asan/lib (pemanggilan make kedua). File yang dapat dieksekusi dari build kedua akan menggantikan file dari build pertama. Dapat dieksekusi yang diinstrumentasi ASan mendapatkan jalur penelusuran library yang berbeda yang mencakup /data/asan/lib sebelum /system/lib melalui penggunaan /system/bin/linker_asan di PT_INTERP.

Sistem build menghapus direktori objek perantara saat nilai $SANITIZE_TARGET telah berubah. Tindakan ini akan memaksa pembangunan ulang semua target sambil mempertahankan biner yang diinstal di /system/lib.

Beberapa target tidak dapat dibuat dengan ASan:

  • File yang dapat dieksekusi yang ditautkan secara statis
  • LOCAL_CLANG:=false target
  • LOCAL_SANITIZE:=false tidak ASan'd untuk SANITIZE_TARGET=address

File yang dapat dieksekusi seperti ini dilewati dalam build SANITIZE_TARGET, dan versi dari pemanggilan make pertama dibiarkan di /system/bin.

Library seperti ini dibuat tanpa ASan. Mereka dapat berisi beberapa kode ASan dari pustaka statis yang menjadi dependensinya.

Dokumentasi pendukung