AddressSanitizer (ASan) è uno strumento veloce basato su compilatore per rilevare bug di memoria nel codice nativo.
ASan rileva:
- Overflow/underflow del buffer dello stack e dell'heap
- Uso dell'heap dopo libero
- Uso dello stack al di fuori dell'ambito
- Doppio libero/selvaggio libero
ASan funziona sia su ARM a 32 bit che a 64 bit, oltre a x86 e x86-64. Il sovraccarico della CPU di ASan è di circa 2x, il sovraccarico della dimensione del codice è compreso tra il 50% e il 2x e un grande sovraccarico di memoria (dipende dai modelli di allocazione, ma nell'ordine di 2x).
Android 10 e il ramo principale AOSP su AArch64 supportano ASan con accelerazione hardware (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 sovraccarico di CPU e dimensioni del codice simile, ma un sovraccarico di RAM molto inferiore (15%). HWASan è non deterministico. Ci sono solo 256 possibili valori di tag, quindi c'è una probabilità fissa dello 0,4% di perdere qualsiasi bug. HWASan non ha le zone rosse di dimensioni limitate di ASan per il rilevamento di overflow e la quarantena a capacità limitata per il rilevamento dell'uso dopo il libero, quindi non importa a HWASan quanto sia grande l'overflow o quanto tempo fa la memoria è stata deallocata. Questo rende HWASan migliore di ASan. Puoi leggere di più sul design di HWASan o sull'uso di HWASan su Android .
ASan rileva gli overflow dello stack/globali oltre agli overflow dell'heap ed è veloce con un sovraccarico di memoria minimo.
Questo documento descrive come costruire ed eseguire parti/tutto di Android con ASan. Se stai creando un'app SDK/NDK con ASan, consulta invece Address Sanitizer .
Sanitizzazione di singoli eseguibili con ASan
Aggiungere LOCAL_SANITIZE:=address
o sanitize: { address: true }
alla regola di compilazione per l'eseguibile. Puoi cercare il codice per esempi esistenti o per trovare gli altri disinfettanti disponibili.
Quando viene rilevato un bug, ASan stampa un rapporto dettagliato sia sullo standard output che su logcat
e quindi blocca il processo.
Sanificazione delle librerie condivise con ASan
A causa del modo in cui funziona ASan, una libreria creata con ASan può essere utilizzata solo da un eseguibile creato con ASan.
Per disinfettare 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 ad Android.mk
per il modulo in questione:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Questo mette la libreria in /system/lib/asan
invece di /system/lib
. Quindi, esegui il tuo eseguibile con:
LD_LIBRARY_PATH=/system/lib/asan
Per i demoni di sistema, aggiungi quanto segue alla sezione appropriata di /init.rc
o /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Verificare che il processo utilizzi le librerie da /system/lib/asan
quando presenti leggendo /proc/$PID/maps
. In caso contrario, potrebbe essere necessario disabilitare 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.
Migliori tracce dello stack
ASan utilizza un veloce unwinder basato su frame-pointer per registrare una traccia dello stack per ogni allocazione di memoria e evento di deallocazione nel programma. La maggior parte di Android è costruita senza puntatori di frame. Di conseguenza, spesso ottieni solo uno o due fotogrammi significativi. Per risolvere questo problema, ricostruisci la libreria con ASan (consigliato!) o con:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Oppure impostare ASAN_OPTIONS=fast_unwind_on_malloc=0
nell'ambiente di elaborazione. Quest'ultimo può richiedere molta 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 il file di origine e le informazioni sulla riga:
- Assicurati che il binario
llvm-symbolizer
sia presente in/system/bin
.llvm-symbolizer
è compilato 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 posizioni file:line
) grazie alla disponibilità di librerie simbolizzate sull'host.
ASan nelle app
ASan non può vedere nel codice Java, ma può rilevare bug nelle librerie JNI. Per questo, devi creare l'eseguibile con ASan, che in questo caso è /system/bin/app_process( 32|64 )
. Ciò abilita ASan in tutte le app sul dispositivo contemporaneamente, il che è un carico pesante, ma un dispositivo con 2 GB di RAM dovrebbe essere in grado di gestirlo.
Aggiungi LOCAL_SANITIZE:=address
alla regola di compilazione app_process
in frameworks/base/cmds/app_process
. Ignora il target app_process__asan
nello stesso file per ora (se è ancora lì nel momento in cui leggi questo).
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 che contengono class main
, anch'esse rientrate della stessa quantità:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Build, sincronizzazione adb, avvio flash fastboot e riavvio.
Usando 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 solo una (o più) app con ASan, scambiando un sovraccarico di memoria per un avvio più lento dell'app.
Questo può essere fatto avviando la tua app con il wrap.
proprietà. 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
riscrive /system/bin/app_process
in /system/bin/asan/app_process
, che è compilato con ASan. Aggiunge anche /system/lib/asan
all'inizio del percorso di ricerca della libreria dinamica. In questo modo le librerie con strumenti ASan da /system/lib/asan
sono preferite alle normali librerie in /system/lib
durante l'esecuzione con asanwrapper
.
Se viene rilevato un bug, l'app si arresta in modo anomalo e il rapporto viene stampato nel registro.
SANITIZE_TARGET
Android 7.0 e versioni successive includono il supporto per la creazione dell'intera piattaforma Android con ASan in una sola volta. (Se stai creando una versione superiore ad Android 9, HWASan è una scelta migliore.)
Esegui i seguenti comandi nello stesso albero di compilazione.
make -j42
SANITIZE_TARGET=address make -j42
In questa modalità, userdata.img
contiene librerie aggiuntive e deve essere visualizzato anche sul dispositivo. Utilizzare la seguente riga di comando:
fastboot flash userdata && fastboot flashall
Questo crea due insiemi di librerie condivise: normale in /system/lib
(la prima invocazione make) e ASan-instrumented in /data/asan/lib
(la seconda invocazione make). Gli eseguibili della seconda build sovrascrivono quelli della prima build. Gli eseguibili con strumenti ASan ottengono un diverso percorso di ricerca della libreria che include /data/asan/lib
prima /system/lib
attraverso l'uso di /system/bin/linker_asan
in PT_INTERP
.
Il sistema di compilazione blocca le directory degli oggetti intermedi quando il valore $SANITIZE_TARGET
è cambiato. Ciò forza una ricostruzione di tutte le destinazioni preservando i binari installati in /system/lib
.
Alcuni obiettivi non possono essere creati con ASan:
- Eseguibili collegati staticamente
-
LOCAL_CLANG:=false
obiettivi -
LOCAL_SANITIZE:=false
non sono ASan'd perSANITIZE_TARGET=address
Gli eseguibili come questi vengono saltati nella build SANITIZE_TARGET
e la versione dalla prima invocazione make viene lasciata in /system/bin
.
Librerie come questa sono costruite senza ASan. Possono contenere del codice ASan dalle librerie statiche da cui dipendono.