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 memori yang besar (bergantung pada pola alokasi Anda, tetapi dalam urutan 2x).
Android 10 dan cabang utama AOSP di AArch64 mendukung Hardware-assist AddressSanitizer (HWASan), alat serupa dengan overhead RAM lebih rendah dan rentang bug terdeteksi yang lebih besar. HWASan mendeteksi penggunaan stack setelah ditampilkan, selain bug yang terdeteksi 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 kemungkinan tetap sebesar 0,4% untuk melewatkan bug. HWASan tidak memiliki zona merah berukuran terbatas ASan untuk mendeteksi overflow dan karantina berkapasitas terbatas untuk mendeteksi use-after-free, sehingga tidak masalah bagi HWASan seberapa besar overflow atau berapa lama memori dibatalkan alokasinya. Ini membuat HWASan lebih baik daripada ASan. Anda dapat membaca lebih lanjut tentang desain HWASan atau tentang penggunaan HWASan di Android.
ASan mendeteksi overflow stack/global selain overflow heap, dan cepat dengan overhead memori minimal.
Dokumen ini menjelaskan cara mem-build dan menjalankan sebagian/semua bagian Android dengan ASan. Jika Anda mem-build 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 menemukan
pembersih lain yang tersedia.
Saat bug terdeteksi, ASan akan mencetak laporan panjang ke output standar dan ke logcat
, lalu membuat error pada prosesnya.
Membersihkan library bersama dengan ASan
Karena cara kerja ASan, library yang di-build dengan ASan hanya dapat digunakan oleh file yang dapat dieksekusi yang di-build dengan ASan.
Untuk membersihkan library bersama yang digunakan di beberapa file yang dapat dieksekusi, yang tidak semuanya
di-build 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
Tindakan ini akan 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 hal berikut ke bagian yang sesuai di
/init.rc
atau /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Pastikan proses tersebut menggunakan library dari /system/lib/asan
saat 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 cepat untuk mencatat pelacakan tumpukan 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, build 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 bisa sangat membutuhkan CPU, tergantung
pada bebannya.
Simbolisasi
Awalnya, laporan ASan berisi referensi ke offset dalam biner dan library umum. Ada dua cara untuk mendapatkan informasi baris dan file sumber:
- Pastikan biner
llvm-symbolizer
ada di/system/bin
.llvm-symbolizer
dibuat dari sumber dithird_party/llvm/tools/llvm-symbolizer
. - Filter laporan melalui skrip
external/compiler-rt/lib/asan/scripts/symbolize.py
.
Pendekatan kedua dapat memberikan lebih banyak data (yaitu, lokasi file:line
) karena
ketersediaan library simbol pada host.
ASan dalam aplikasi
ASan tidak dapat melihat kode Java, tetapi dapat mendeteksi bug dalam library
JDK. Untuk itu, Anda perlu mem-build file yang dapat dieksekusi dengan ASan, yang dalam
hal ini adalah /system/bin/app_process(32|64)
. Hal ini
memungkinkan ASan di semua aplikasi pada perangkat secara bersamaan, yang merupakan
beban berat, tetapi perangkat dengan RAM 2 GB seharusnya dapat menangani hal ini.
Tambahkan LOCAL_SANITIZE:=address
ke
aturan build app_process
di frameworks/base/cmds/app_process
. Abaikan target app_process__asan
dalam file yang sama untuk saat ini (jika target tersebut masih ada saat Anda membaca ini).
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
, yang 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, booting flash fastboot, dan mulai ulang.
Menggunakan properti wrap
Pendekatan di bagian sebelumnya menempatkan ASan ke dalam setiap aplikasi dalam sistem (sebenarnya, ke dalam setiap turunan proses Zygote). Anda hanya dapat menjalankan satu (atau beberapa) aplikasi dengan ASan, yang menukar beberapa overhead memori untuk startup aplikasi yang lebih lambat.
Hal ini dapat dilakukan dengan memulai aplikasi Anda dengan properti wrap.
.
Contoh berikut menjalankan aplikasi Gmail di 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
ke /system/bin/asan/app_process
, yang dibuat dengan
ASan. Kode ini juga menambahkan /system/lib/asan
di awal jalur penelusuran library dinamis. Dengan cara ini, library yang diinstrumentasi ASan
dari /system/lib/asan
lebih disukai daripada library normal
di /system/lib
saat berjalan dengan asanwrapper
.
Jika bug ditemukan, aplikasi akan error, dan laporan akan dicetak ke log.
SANITKAN_TARGET
Android 7.0 dan yang lebih tinggi menyertakan dukungan untuk mem-build seluruh platform Android dengan ASan sekaligus. (Jika Anda membangun rilis yang lebih tinggi dari Android 9, HWASan adalah pilihan yang lebih baik.)
Jalankan perintah berikut di hierarki 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 kumpulan library bersama: normal di
/system/lib
(pemanggilan make pertama), dan berinstrumen ASan di
/data/asan/lib
(pemanggilan make kedua). File yang dapat dieksekusi dari
build kedua akan menimpa file dari build pertama. File yang dapat dijalankan
dengan instrumentasi ASan mendapatkan jalur penelusuran library yang berbeda yang menyertakan
/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 pembuatan ulang semua
target sekaligus mempertahankan biner yang diinstal di /system/lib
.
Beberapa target tidak dapat dibuat dengan ASan:
- File yang dapat dieksekusi yang ditautkan secara statis
- Target
LOCAL_CLANG:=false
LOCAL_SANITIZE:=false
tidak di-ASAN untukSANITIZE_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. File ini dapat berisi beberapa kode ASan dari library statis yang menjadi dependensinya.