Pembersih Alamat

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 di third_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 untuk SANITIZE_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.

Dokumentasi pendukung