AddressSanitizer (ASan) è uno strumento rapido basato sul compilatore per rilevare bug di memoria nel codice nativo.
ASan rileva:
- Overflow/underflow del buffer dello stack e dell'heap
- Heap use-after-free
- Utilizzo dello stack al di fuori dell'ambito
- Double free/wild free
ASan viene eseguito su ARM a 32 e 64 bit, oltre a x86 e x86-64. Il sovraccarico della CPU di ASan è circa il doppio, il sovraccarico delle dimensioni del codice è compreso tra il 50% e il doppio e un grande sovraccarico di memoria (a seconda dei pattern di allocazione, ma nell'ordine del doppio).
Android 10 e l'ultimo ramo di rilascio AOSP su AArch64 supportano Hardware-assisted AddressSanitizer (HWASan), uno strumento simile con un sovraccarico di RAM inferiore e una gamma più ampia di bug rilevati. HWASan rileva l'utilizzo dello stack dopo il ritorno, oltre ai bug rilevati da ASan.
HWASan ha un overhead simile per CPU e dimensioni del codice, ma un overhead della RAM molto più piccolo (15%). HWASan è non deterministico. Esistono solo 256 valori di tag possibili, quindi c'è una probabilità fissa dello 0,4% di non rilevare alcun bug. HWASan non dispone delle zone rosse di dimensioni limitate di ASan per rilevare gli overflow e della quarantena a capacità limitata per rilevare l'uso dopo la liberazione, quindi per HWASan non importa quanto sia grande l'overflow o quanto tempo fa è stata deallocata la memoria. Ciò rende HWASan migliore di ASan. Puoi leggere di più sulla progettazione di HWASan o sull'utilizzo di HWASan su Android.
ASan rileva overflow di stack/globali oltre agli overflow di heap ed è veloce con un sovraccarico di memoria minimo.
Questo documento descrive come compilare ed eseguire parti o tutto Android con ASan. Se stai creando un'app SDK/NDK con ASan, consulta AddressSanitizer.
Sanitizzare singoli eseguibili con ASan
Aggiungi LOCAL_SANITIZE:=address
o sanitize: { address: true }
alla regola di compilazione per l'eseguibile. Puoi cercare nel codice esempi esistenti o trovare
gli altri sanificatori disponibili.
Quando viene rilevato un bug, ASan stampa un report dettagliato sia nell'output standard sia in logcat
e poi arresta il processo in modo anomalo.
Sanitizzare le librerie condivise con ASan
A causa del funzionamento di ASan, una libreria creata con ASan può essere utilizzata solo da un eseguibile creato con ASan.
Per sanificare una libreria condivisa utilizzata in più eseguibili, non tutti creati con ASan, sono necessarie due copie della libreria. Il modo consigliato per farlo è aggiungere quanto segue a Android.mk
per il modulo in questione:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
In questo modo la libreria viene inserita in /system/lib/asan
anziché in
/system/lib
. Quindi, esegui l'eseguibile con:
LD_LIBRARY_PATH=/system/lib/asan
Per i daemon di sistema, aggiungi quanto segue alla sezione appropriata di
/init.rc
o /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Verifica che il processo utilizzi le librerie di /system/lib/asan
quando presenti leggendo /proc/$PID/maps
. In caso contrario, potresti dover
disattivare 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.
Analisi dello stack migliori
ASan utilizza un unwinder rapido basato su frame pointer per registrare una traccia dello stack per ogni evento di allocazione e deallocazione della memoria nel programma. La maggior parte di Android è creata senza puntatori di frame. Di conseguenza, spesso ottieni solo uno o due fotogrammi significativi. Per risolvere il problema, ricompila la libreria con ASan (consigliato) o con:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
o imposta ASAN_OPTIONS=fast_unwind_on_malloc=0
nell'ambiente
di elaborazione. Questi ultimi possono richiedere un utilizzo elevato della CPU, a seconda
del carico.
Simbolizzazione
Inizialmente, i report ASan contengono riferimenti agli offset nei file binari e nelle librerie condivise. Esistono due modi per ottenere informazioni sul file sorgente e sulla riga:
- Assicurati che il binario
llvm-symbolizer
sia presente in/system/bin
.llvm-symbolizer
è creato da sorgenti inthird_party/llvm/tools/llvm-symbolizer
. - Filtra il report tramite lo script
external/compiler-rt/lib/asan/scripts/symbolize.py
.
Il secondo approccio può fornire più dati (ovvero file:line
località) grazie alla disponibilità di librerie simbolizzate sull'host.
ASan nelle app
ASan non può vedere il codice Java, ma può rilevare bug nelle librerie JNI. Per farlo, devi creare l'eseguibile con ASan, che in
questo caso è /system/bin/app_process(32|64)
. In questo modo, ASan viene attivato contemporaneamente in tutte le app sul dispositivo, il che comporta un carico elevato, ma un dispositivo con 2 GB di RAM dovrebbe essere in grado di gestirlo.
Aggiungi LOCAL_SANITIZE:=address
alla
regola di build app_process
in frameworks/base/cmds/app_process
.
Modifica la sezione service zygote
del file system/core/rootdir/init.zygote(32|64).rc
appropriato per aggiungere le seguenti righe al blocco di righe rientrate contenenti class main
, rientrate anche queste della stessa quantità:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Build, adb sync, fastboot flash boot e riavvio.
Utilizzare la proprietà wrap
L'approccio nella sezione precedente inserisce ASan in ogni app del sistema (in realtà, in ogni discendente del processo Zygote). È possibile eseguire una sola app (o più app) con ASan, sacrificando un po' di memoria per un avvio più lento dell'app.
Per farlo, avvia l'app con la proprietà wrap.
.
L'esempio seguente esegue l'app Gmail in ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
In questo contesto, asanwrapper
viene riscritto in /system/bin/app_process
in /system/bin/asan/app_process
, che è creato con
ASan. Aggiunge anche /system/lib/asan
all'inizio del percorso di ricerca della libreria dinamica. In questo modo, le librerie strumentate con ASan di /system/lib/asan
vengono preferite alle librerie normali di /system/lib
quando vengono eseguite con asanwrapper
.
Se viene trovato un bug, l'app si arresta in modo anomalo e il report viene stampato nel log.
SANITIZE_TARGET
Android 7.0 e versioni successive includono il supporto per la creazione dell'intera piattaforma Android con ASan contemporaneamente. Se stai creando una release superiore ad Android 9, HWASan è una scelta migliore.
Esegui i seguenti comandi nello stesso albero di build.
make -j42
SANITIZE_TARGET=address make -j42
In questa modalità, userdata.img
contiene librerie aggiuntive e deve essere
flashato anche sul dispositivo. Utilizza la seguente riga di comando:
fastboot flash userdata && fastboot flashall
Vengono creati due set di librerie condivise: normali in
/system/lib
(la prima chiamata di make) e strumentate con ASan in
/data/asan/lib
(la seconda chiamata di make). Gli eseguibili della
seconda build sovrascrivono quelli della prima build. Gli eseguibili strumentati con ASan
ottengono un percorso di ricerca della libreria diverso che include
/data/asan/lib
prima di /system/lib
tramite l'utilizzo di
/system/bin/linker_asan
in PT_INTERP
.
Il sistema di compilazione sovrascrive le directory degli oggetti intermedi quando il valore di $SANITIZE_TARGET
è cambiato. In questo modo viene forzata la ricompilazione di tutti i target mantenendo i binari installati in /system/lib
.
Alcuni target non possono essere creati con ASan:
- Eseguibili collegati staticamente
LOCAL_CLANG:=false
targetLOCAL_SANITIZE:=false
non sono ASan'd perSANITIZE_TARGET=address
Gli eseguibili come questi vengono ignorati nella build SANITIZE_TARGET
e la versione della prima chiamata make viene lasciata in /system/bin
.
Librerie come questa vengono create senza ASan. Possono contenere codice ASan dalle librerie statiche da cui dipendono.