AddressSanitizer (ASan) adalah alat berbasis kompiler cepat untuk mendeteksi bug memori dalam kode asli.
ASan mendeteksi:
- Stack dan heap buffer overflow/underflow
- Tumpukan penggunaan setelah gratis
- Stack digunakan di luar ruang lingkup
- Bebas ganda/bebas liar
ASan berjalan pada ARM 32-bit dan 64-bit, ditambah x86 dan x86-64. Overhead CPU ASan kira-kira 2x, overhead ukuran kode antara 50% dan 2x, dan overhead memori yang besar (tergantung pada pola alokasi Anda, tetapi pada urutan 2x).
Android 10 dan cabang master AOSP di AArch64 mendukung hardware-accelerated ASan (HWASan) , alat serupa dengan overhead RAM yang lebih rendah dan rentang bug yang terdeteksi lebih besar. HWASan mendeteksi penggunaan tumpukan setelah kembali, selain bug yang terdeteksi oleh ASan.
HWASan memiliki overhead CPU dan ukuran kode yang serupa, tetapi overhead RAM yang jauh lebih kecil (15%). HWASan adalah nondeterministik. Hanya ada 256 kemungkinan nilai tag, jadi ada kemungkinan 0,4% untuk kehilangan bug apa pun. HWASan tidak memiliki zona merah ukuran terbatas ASan untuk mendeteksi luapan dan karantina berkapasitas terbatas untuk mendeteksi penggunaan-setelah-bebas, jadi tidak masalah bagi HWASan seberapa besar luapan atau berapa lama memori tidak dialokasikan. Hal ini membuat HWASan lebih baik dari ASan. Anda dapat membaca lebih lanjut tentang desain HWASan atau tentang penggunaan HWASan di Android .
ASan mendeteksi luapan tumpukan/global selain luapan tumpukan, dan cepat dengan overhead memori minimal.
Dokumen ini menjelaskan cara membangun dan menjalankan sebagian/semua Android dengan ASan. Jika Anda membuat aplikasi SDK/NDK dengan ASan, lihat Pembersih Alamat .
Membersihkan executable individu dengan ASan
Tambahkan LOCAL_SANITIZE:=address
atau sanitize: { address: true }
ke aturan build untuk executable. Anda dapat mencari kode untuk contoh yang ada atau untuk menemukan pembersih lain yang tersedia.
Saat bug terdeteksi, ASan mencetak laporan verbose baik ke output standar maupun ke logcat
dan kemudian membuat proses crash.
Membersihkan perpustakaan bersama dengan ASan
Karena cara kerja ASan, perpustakaan yang dibangun dengan ASan hanya dapat digunakan oleh executable yang dibangun dengan ASan.
Untuk membersihkan perpustakaan bersama yang digunakan di beberapa executable, tidak semuanya dibuat dengan ASan, Anda memerlukan dua salinan perpustakaan. Cara yang disarankan untuk melakukannya adalah dengan menambahkan yang berikut ini ke Android.mk
untuk modul yang dimaksud:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Ini menempatkan perpustakaan di /system/lib/asan
alih-alih /system/lib
. Kemudian, jalankan executable Anda dengan:
LD_LIBRARY_PATH=/system/lib/asan
Untuk daemon sistem, tambahkan berikut ini ke bagian yang sesuai dari /init.rc
atau /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Verifikasi bahwa proses menggunakan perpustakaan 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.
Jejak tumpukan yang lebih baik
ASan menggunakan unwinder berbasis frame-pointer yang cepat untuk merekam jejak tumpukan untuk setiap alokasi memori dan peristiwa deallokasi dalam program. Sebagian besar Android dibangun tanpa penunjuk bingkai. Akibatnya, Anda sering kali hanya mendapatkan satu atau dua bingkai yang bermakna. Untuk memperbaikinya, buat ulang perpustakaan dengan ASan (disarankan!), atau dengan:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Atau atur ASAN_OPTIONS=fast_unwind_on_malloc=0
di lingkungan proses. Yang terakhir bisa sangat intensif CPU, tergantung pada bebannya.
Simbolisasi
Awalnya, laporan ASan berisi referensi ke offset di binari dan perpustakaan bersama. Ada dua cara untuk mendapatkan file sumber dan informasi baris:
- Pastikan biner
llvm-symbolizer
ada di/system/bin
.llvm-symbolizer
dibangun dari sumber dithird_party/llvm/tools/llvm-symbolizer
. - Filter laporan melalui
external/compiler-rt/lib/asan/scripts/symbolize.py
.
Pendekatan kedua dapat menyediakan lebih banyak data (yaitu, lokasi file:line
) karena ketersediaan perpustakaan yang disimbolkan pada host.
ASan di aplikasi
ASan tidak dapat melihat kode Java, tetapi dapat mendeteksi bug di perpustakaan JNI. Untuk itu, Anda perlu membangun executable dengan ASan, yang dalam hal ini adalah /system/bin/app_process( 32|64 )
. Ini memungkinkan ASan di semua aplikasi pada perangkat secara bersamaan, yang merupakan beban berat, tetapi perangkat dengan RAM 2 GB harus dapat menangani ini.
Tambahkan LOCAL_SANITIZE:=address
ke aturan pembuatan app_process
di frameworks/base/cmds/app_process
. Abaikan target app_process__asan
dalam file yang sama untuk saat ini (jika 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 indentasi yang berisi class main
, juga indentasi dengan jumlah yang sama:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Bangun, sinkronisasi adb, boot flash fastboot, dan reboot.
Menggunakan properti bungkus
Pendekatan di bagian sebelumnya menempatkan ASan ke dalam setiap aplikasi dalam sistem (sebenarnya, ke setiap turunan dari proses Zygote). Dimungkinkan untuk menjalankan hanya satu (atau beberapa) aplikasi dengan ASan, menukar beberapa overhead memori untuk startup aplikasi yang lebih lambat.
Ini dapat dilakukan dengan memulai aplikasi Anda dengan wrap.
Properti. 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
ke /system/bin/asan/app_process
, yang dibangun dengan ASan. Itu juga menambahkan /system/lib/asan
di awal jalur pencarian perpustakaan dinamis. Dengan cara ini pustaka yang diinstrumentasi ASan dari /system/lib/asan
lebih disukai daripada pustaka normal di /system/lib
saat dijalankan dengan asanwrapper
.
Jika bug ditemukan, aplikasi mogok, dan laporan dicetak ke log.
SANITIZE_TARGET
Android 7.0 dan yang lebih tinggi menyertakan dukungan untuk membangun seluruh platform Android dengan ASan sekaligus. (Jika Anda membuat rilis 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 perpustakaan tambahan dan harus di-flash ke perangkat juga. Gunakan baris perintah berikut:
fastboot flash userdata && fastboot flashall
Ini membangun dua set pustaka bersama: normal di /system/lib
(permintaan make pertama), dan ASan-instrumented di /data/asan/lib
(doa make kedua). Eksekusi dari build kedua menimpa yang dari build pertama. Eksekusi yang diinstrumentasi ASan mendapatkan jalur pencarian perpustakaan berbeda yang menyertakan /data/asan/lib
sebelum /system/lib
melalui penggunaan /system/bin/linker_asan
di PT_INTERP
.
Sistem pembangunan menghancurkan direktori objek perantara ketika nilai $SANITIZE_TARGET
telah berubah. Ini memaksa pembangunan kembali semua target sambil mempertahankan binari yang diinstal di bawah /system/lib
.
Beberapa target tidak dapat dibangun dengan ASan:
- Eksekusi yang terhubung secara statis
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
tidak ASan untukSANITIZE_TARGET=address
Eksekusi seperti ini dilewati dalam SANITIZE_TARGET
build, dan versi dari pemanggilan make pertama dibiarkan di /system/bin
.
Perpustakaan seperti ini dibangun tanpa ASan. Mereka dapat berisi beberapa kode ASan dari perpustakaan statis yang mereka andalkan.