AddressSanitizer (ASan) ist ein schnelles compilerbasiertes Tool zum Erkennen von Speicherfehlern in nativem Code.
ASan erkennt:
- Stack- und Heap-Pufferüberlauf/-unterlauf
- Heap-Nutzung nach dem Freigeben
- Stack-Nutzung außerhalb des Anwendungsbereichs
- Double Free/Wild Free
ASan wird sowohl auf 32-Bit- als auch auf 64-Bit-ARM sowie auf x86 und x86-64 ausgeführt. Der CPU-Overhead von ASan beträgt etwa das Doppelte, der Code-Overhead zwischen 50% und dem Doppelten und der Speicher-Overhead ist hoch (abhängig von Ihren Zuweisungsmustern, aber in der Größenordnung des Doppelten).
Android 10 und der aktuelle AOSP-Release-Branch auf AArch64 unterstützen Hardware-assisted AddressSanitizer (HWASan), ein ähnliches Tool mit geringerem RAM-Overhead und einer größeren Anzahl erkannter Fehler. HWASan erkennt zusätzlich zu den von ASan erkannten Fehlern auch die Verwendung des Stacks nach der Rückgabe.
HWASan hat einen ähnlichen CPU- und Codegrößen-Overhead, aber einen viel geringeren RAM-Overhead (15%). HWASan ist nicht deterministisch. Es gibt nur 256 mögliche Tag-Werte.Die Wahrscheinlichkeit, dass ein Fehler nicht erkannt wird, liegt also bei 0,4 %. HWASan hat nicht die rot markierten Zonen mit begrenzter Größe von ASan zum Erkennen von Überläufen und die Quarantäne mit begrenzter Kapazität zum Erkennen von Use-After-Free-Fehlern. Daher spielt es für HWASan keine Rolle, wie groß der Überlauf ist oder wie lange der Speicher schon freigegeben wurde. Das macht HWASan besser als ASan. Weitere Informationen zum Design von HWASan und zur Verwendung von HWASan auf Android
ASan erkennt neben Heap-Überläufen auch Stack-/Global-Überläufe und ist schnell mit minimalem Arbeitsspeicheraufwand.
In diesem Dokument wird beschrieben, wie Sie Teile oder das gesamte Android mit ASan erstellen und ausführen. Wenn Sie eine SDK-/NDK-App mit ASan erstellen, lesen Sie stattdessen Address Sanitizer.
Einzelne ausführbare Dateien mit ASan bereinigen
Fügen Sie der Build-Regel für die ausführbare Datei LOCAL_SANITIZE:=address
oder sanitize: { address: true }
hinzu. Sie können im Code nach vorhandenen Beispielen oder nach anderen verfügbaren Bereinigungsfunktionen suchen.
Wenn ein Fehler erkannt wird, gibt ASan einen ausführlichen Bericht sowohl in der Standardausgabe als auch in logcat
aus und lässt den Prozess dann abstürzen.
Gemeinsam genutzte Bibliotheken mit ASan bereinigen
Aufgrund der Funktionsweise von ASan kann eine mit ASan erstellte Bibliothek nur von einer ausführbaren Datei verwendet werden, die mit ASan erstellt wurde.
Wenn Sie eine gemeinsam genutzte Bibliothek bereinigen möchten, die in mehreren ausführbaren Dateien verwendet wird, von denen nicht alle mit ASan erstellt wurden, benötigen Sie zwei Kopien der Bibliothek. Die empfohlene Vorgehensweise ist, Folgendes für das betreffende Modul zu Android.mk
hinzuzufügen:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Dadurch wird die Bibliothek in /system/lib/asan
anstelle von /system/lib
platziert. Führen Sie dann Ihre ausführbare Datei mit folgendem Befehl aus:
LD_LIBRARY_PATH=/system/lib/asan
Fügen Sie für System-Daemons Folgendes in den entsprechenden Abschnitt von /init.rc
oder /init.$device$.rc
ein.
setenv LD_LIBRARY_PATH /system/lib/asan
Prüfen Sie, ob der Prozess Bibliotheken aus /system/lib/asan
verwendet, indem Sie /proc/$PID/maps
lesen. Wenn nicht, müssen Sie SELinux möglicherweise 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 für jedes Speicherzuweisungs- und ‑freigabeereignis im Programm einen Stacktrace aufzuzeichnen. Die meisten Android-Versionen werden ohne Frame-Pointer erstellt. Daher erhalten Sie oft nur ein oder zwei aussagekräftige Frames. Um dieses Problem zu beheben, müssen Sie die Bibliothek entweder mit ASan (empfohlen) oder mit Folgendem neu erstellen:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Oder legen Sie ASAN_OPTIONS=fast_unwind_on_malloc=0
in der Prozessumgebung fest. Letzteres kann je nach Last sehr CPU-intensiv sein.
Symbolisierung
ASan-Berichte enthalten anfangs Verweise auf Offsets in Binärdateien und gemeinsam genutzten Bibliotheken. Es gibt zwei Möglichkeiten, Informationen zu Quelldateien und ‑zeilen zu erhalten:
- Die Binärdatei
llvm-symbolizer
muss in/system/bin
vorhanden sein.llvm-symbolizer
wird aus Quellen inthird_party/llvm/tools/llvm-symbolizer
erstellt. - Filtern Sie den Bericht über das
external/compiler-rt/lib/asan/scripts/symbolize.py
-Skript.
Der zweite Ansatz kann mehr Daten (d. h. file:line
-Standorte) liefern, da symbolisierte Bibliotheken auf dem Host verfügbar sind.
ASan in Apps
ASan kann nicht in Java-Code sehen, aber Fehler in den JNI-Bibliotheken erkennen. Dazu müssen Sie die ausführbare Datei mit ASan erstellen, in diesem Fall /system/bin/app_process(32|64)
. Dadurch wird ASan in allen Apps auf dem Gerät gleichzeitig aktiviert, was eine hohe Belastung darstellt. Ein Gerät mit 2 GB RAM sollte dies jedoch bewältigen können.
Fügen Sie LOCAL_SANITIZE:=address
der app_process
-Build-Regel in frameworks/base/cmds/app_process
hinzu.
Bearbeiten Sie den Abschnitt service zygote
der entsprechenden Datei system/core/rootdir/init.zygote(32|64).rc
, um dem Block eingerückter Zeilen, der class main
enthält, die folgenden Zeilen hinzuzufügen. Die neuen Zeilen müssen ebenfalls um denselben Betrag eingerückt werden:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Erstellen, mit ADB synchronisieren, mit Fastboot flashen und neu starten.
„wrap“-Property verwenden
Bei der Methode im vorherigen Abschnitt wird ASan in jede App im System eingefügt (genauer gesagt in jeden Nachfolger des Zygote-Prozesses). Es ist möglich, nur eine (oder mehrere) Apps mit ASan auszuführen. Dabei wird etwas mehr Speicher benötigt, was zu einem langsameren App-Start führt.
Dazu müssen Sie Ihre App mit der Property wrap.
starten.
Im folgenden Beispiel wird die Gmail App unter ASan ausgeführt:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
In diesem Kontext schreibt asanwrapper
/system/bin/app_process
in /system/bin/asan/app_process
um, das mit ASan erstellt wurde. Außerdem wird /system/lib/asan
am Anfang des dynamischen Bibliotheksuchpfads hinzugefügt. So werden ASan-instrumentierte Bibliotheken aus /system/lib/asan
gegenüber normalen Bibliotheken in /system/lib
bevorzugt, wenn sie mit asanwrapper
ausgeführt werden.
Wenn ein Fehler gefunden wird, stürzt die App ab und der Bericht wird im Log ausgegeben.
SANITIZE_TARGET
Android 7.0 und höher bietet Unterstützung für das gleichzeitige Erstellen der gesamten Android-Plattform mit ASan. Wenn Sie ein Release für eine höhere Android-Version als Android 9 erstellen, ist HWASan 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 Gruppen von gemeinsam genutzten Bibliotheken erstellt: normale in /system/lib
(erster make-Aufruf) und ASan-instrumentierte in /data/asan/lib
(zweiter make-Aufruf). Die ausführbaren Dateien aus dem zweiten Build überschreiben die aus dem ersten Build. Für ASan-instrumentierte ausführbare Dateien wird ein anderer Bibliothekssuchpfad verwendet, der /data/asan/lib
vor /system/lib
enthält. Dies wird durch die Verwendung von /system/bin/linker_asan
in PT_INTERP
erreicht.
Das Build-System überschreibt Zwischenobjektverzeichnisse, wenn sich der Wert von $SANITIZE_TARGET
geändert hat. Dadurch wird ein erneuter Build aller Ziele erzwungen, während installierte Binärdateien unter /system/lib
beibehalten werden.
Einige Ziele können nicht mit ASan erstellt werden:
- Statisch verknüpfte ausführbare Dateien
LOCAL_CLANG:=false
ZieleLOCAL_SANITIZE:=false
sind nicht ASan-kompatibel fürSANITIZE_TARGET=address
Ausführbare Dateien wie diese werden beim SANITIZE_TARGET
-Build übersprungen und die Version aus dem ersten „make“-Aufruf verbleibt in /system/bin
.
Solche Bibliotheken werden ohne ASan erstellt. Sie können ASan-Code aus den statischen Bibliotheken enthalten, von denen sie abhängen.