AddressSanitizer (ASan) ist ein schnelles Compiler-basiertes Tool zum Erkennen von Speicherfehlern in nativem Code.
ASan erkennt:
- Stack- und Heap-Pufferüberlauf/-unterlauf
- Heap-Nutzung nach kostenlos
- Stack-Verwendung außerhalb des Gültigkeitsbereichs
- Doppelt frei/wild frei
ASan läuft sowohl auf 32-Bit- als auch auf 64-Bit-ARM sowie auf x86 und x86-64. Der CPU-Overhead von ASan beträgt ungefähr das Doppelte, der Overhead für die Codegröße liegt zwischen 50% und dem Doppelten und ein großer Speicher-Overhead (abhängig von Ihren Zuweisungsmustern, aber in der Größenordnung von 2x).
Android 10 und der AOSP-Master-Zweig auf AArch64 unterstützen hardwarebeschleunigtes ASan (HWASan) , ein ähnliches Tool mit geringerem RAM-Overhead und einer größeren Auswahl an erkannten Fehlern. HWASan erkennt die Stack-Nutzung nach der Rückgabe zusätzlich zu den von ASan erkannten Fehlern.
HWASan hat einen ähnlichen CPU- und Codegrößen-Overhead, aber einen viel geringeren RAM-Overhead (15 %). HWAAn ist nicht deterministisch. Es gibt nur 256 mögliche Tag-Werte, also gibt es eine pauschale Wahrscheinlichkeit von 0,4 %, einen Fehler zu übersehen. HWASan verfügt nicht über die begrenzten roten Zonen von ASan zum Erkennen von Überläufen und Quarantäne mit begrenzter Kapazität zum Erkennen von Use-after-free, daher spielt es für HWASan keine Rolle, wie groß der Überlauf ist oder wie lange es her ist, dass der Speicher freigegeben wurde. Das macht HWAAn besser als ASan. Sie können mehr über das Design von HWAAn oder über die Verwendung von HWAAn auf Android lesen .
ASan erkennt Stapel-/globale Überläufe zusätzlich zu Heap-Überläufen und ist schnell mit minimalem Speicher-Overhead.
Dieses Dokument beschreibt, wie man Teile/alles von Android mit ASan erstellt und ausführt. Wenn Sie eine SDK/NDK-App mit ASan erstellen, lesen Sie stattdessen Address Sanitizer .
Einzelne ausführbare Dateien mit ASan bereinigen
Fügen Sie LOCAL_SANITIZE:=address
oder sanitize: { address: true }
zur Build-Regel für die ausführbare Datei hinzu. Sie können den Code nach vorhandenen Beispielen durchsuchen oder andere verfügbare Desinfektionsmittel finden.
Wenn ein Fehler erkannt wird, gibt ASan einen ausführlichen Bericht sowohl an die Standardausgabe als auch an logcat
und stürzt dann den Prozess ab.
Gemeinsam genutzte Bibliotheken mit ASan bereinigen
Aufgrund der Funktionsweise von ASan kann eine mit ASan erstellte Bibliothek nur von einer mit ASan erstellten ausführbaren Datei verwendet werden.
Um eine gemeinsam genutzte Bibliothek zu bereinigen, die in mehreren ausführbaren Dateien verwendet wird, von denen nicht alle mit ASan erstellt wurden, benötigen Sie zwei Kopien der Bibliothek. Der empfohlene Weg, dies zu tun, besteht darin, Folgendes zu Android.mk
für das betreffende Modul hinzuzufügen:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Dadurch wird die Bibliothek in /system/lib/asan
anstelle von /system/lib
abgelegt. Führen Sie dann Ihre ausführbare Datei aus mit:
LD_LIBRARY_PATH=/system/lib/asan
Fügen Sie für System-Daemons Folgendes zum entsprechenden Abschnitt von /init.rc
oder /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Überprüfen Sie, ob der Prozess Bibliotheken aus /system/lib/asan
, falls vorhanden, indem Sie /proc/$PID/maps
lesen. Wenn dies nicht der Fall ist, müssen Sie möglicherweise SELinux deaktivieren:
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.
Bessere Stacktraces
ASan verwendet einen schnellen, Frame-Pointer-basierten Unwinder, um einen Stack-Trace für jedes Speicherzuweisungs- und Freigabeereignis im Programm aufzuzeichnen. Der größte Teil von Android ist ohne Frame-Zeiger aufgebaut. Dadurch erhalten Sie oft nur ein oder zwei aussagekräftige Frames. Um dies zu beheben, erstellen Sie die Bibliothek entweder mit ASan (empfohlen!) oder mit:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Oder setzen ASAN_OPTIONS=fast_unwind_on_malloc=0
in der Prozessumgebung. Letzteres kann je nach Auslastung sehr CPU-intensiv sein.
Symbolisierung
Anfänglich enthalten ASan-Berichte Verweise auf Offsets in Binärdateien und gemeinsam genutzten Bibliotheken. Es gibt zwei Möglichkeiten, Quelldatei- und Zeileninformationen zu erhalten:
- Stellen Sie sicher, dass die
llvm-symbolizer
Binärdatei in/system/bin
vorhanden ist.llvm-symbolizer
wird aus Quellen inthird_party/llvm/tools/llvm-symbolizer
. - Filtern Sie den Bericht über das Skript
external/compiler-rt/lib/asan/scripts/symbolize.py
.
Der zweite Ansatz kann aufgrund der Verfügbarkeit symbolisierter Bibliotheken auf dem Host mehr Daten bereitstellen (d. h. file:line
Speicherorte).
ASan in Apps
ASan kann den Java-Code nicht einsehen, aber Fehler in den JNI-Bibliotheken erkennen. Dazu müssen Sie die ausführbare Datei mit ASan erstellen, die in diesem Fall /system/bin/app_process( 32|64 )
ist. Dadurch wird ASan in allen Apps auf dem Gerät gleichzeitig aktiviert, was eine starke Last ist, aber ein Gerät mit 2 GB RAM sollte damit umgehen können.
Fügen Sie LOCAL_SANITIZE:=address
zur Build-Regel app_process
in frameworks/base/cmds/app_process
. Ignorieren Sie vorerst das Ziel app_process__asan
in derselben Datei (falls es zum Zeitpunkt des Lesens noch vorhanden ist).
Bearbeiten Sie den Abschnitt service zygote
zygote der entsprechenden Datei system/core/rootdir/init.zygote( 32|64 ).rc
, um die folgenden Zeilen zu dem Block eingerückter Zeilen hinzuzufügen, die class main
enthalten, ebenfalls um denselben Betrag eingerückt:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Build, Adb-Sync, Fastboot-Flash-Boot und Neustart.
Verwenden der Wrap-Eigenschaft
Der Ansatz im vorherigen Abschnitt fügt ASan in jede App im System ein (eigentlich in jeden Nachkommen des Zygote-Prozesses). Es ist möglich, nur eine (oder mehrere) Apps mit ASan auszuführen, wobei etwas Speicheraufwand für einen langsameren App-Start eingetauscht wird.
Dies kann erfolgen, indem Sie Ihre App mit dem wrap.
Eigentum. Das folgende Beispiel führt die Gmail-App unter ASan aus:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
In diesem Zusammenhang schreibt asanwrapper
/system/bin/app_process
in /system/bin/asan/app_process
um, das mit ASan gebaut wird. Außerdem /system/lib/asan
am Anfang des Suchpfads für dynamische Bibliotheken hinzugefügt. Auf diese Weise werden ASan-instrumentierte Bibliotheken aus /system/lib/asan
gegenüber normalen Bibliotheken in /system/lib
bevorzugt, wenn sie mit asanwrapper
.
Wenn ein Fehler gefunden wird, stürzt die App ab und der Bericht wird im Protokoll gedruckt.
SANITIZE_TARGET
Android 7.0 und höher beinhaltet Unterstützung für den Aufbau der gesamten Android-Plattform mit ASan auf einmal. (Wenn Sie eine höhere Version als Android 9 erstellen, ist HWAAn die bessere Wahl.)
Führen Sie die folgenden Befehle im selben Build-Baum aus.
make -j42
SANITIZE_TARGET=address make -j42
In diesem Modus enthält userdata.img
zusätzliche Bibliotheken und muss ebenfalls auf das Gerät geflasht werden. Verwenden Sie die folgende Befehlszeile:
fastboot flash userdata && fastboot flashall
Dadurch werden zwei Sätze gemeinsam genutzter Bibliotheken erstellt: normal in /system/lib
(der erste make-Aufruf) und ASan-instrumentiert in /data/asan/lib
(der zweite make-Aufruf). Ausführbare Dateien aus dem zweiten Build überschreiben die aus dem ersten Build. ASan-instrumentierte ausführbare Dateien erhalten einen anderen Bibliothekssuchpfad, der /data/asan/lib
vor /system/lib
durch die Verwendung von /system/bin/linker_asan
in PT_INTERP
.
Das Build-System löscht Zwischenobjektverzeichnisse, wenn sich der Wert von $SANITIZE_TARGET
geändert hat. Dies erzwingt eine Neuerstellung aller Ziele, während installierte Binärdateien unter /system/lib
.
Einige Ziele können nicht mit ASan erstellt werden:
- Statisch verknüpfte ausführbare Dateien
-
LOCAL_CLANG:=false
Ziele -
LOCAL_SANITIZE:=false
wird nicht fürSANITIZE_TARGET=address
Ausführbare Dateien wie diese werden im SANITIZE_TARGET
Build übersprungen, und die Version vom ersten make-Aufruf wird in /system/bin
belassen.
Bibliotheken wie diese werden ohne ASan erstellt. Sie können ASan-Code aus den statischen Bibliotheken enthalten, von denen sie abhängen.