AddressSanitizer (ASan) ist ein schnelles compilerbasiertes Tool zum Erkennen von Speicherfehlern in nativem Code.
ASan erkennt Folgendes:
- Stack- und Heap-Pufferüberläufe/-unterläufe
- Heap-Nutzung nach Freigabe
- Stack-Nutzung außerhalb des Bereichs
- Doppelte Freigabe/Wild Free
ASan wird sowohl auf 32-Bit- als auch auf 64-Bit-ARM sowie auf x86 und x86-64 ausgeführt. Der CPU-Aufwand von ASan beträgt etwa das Doppelte, der Code-Aufwand liegt zwischen 50% und dem Doppelten und der Speicher-Aufwand ist hoch (abhängig von Ihren Zuweisungsmustern, aber in der Größenordnung des Doppelten).
Android 10 und der aktuelle Release-Branch von AOSP auf AArch64 unterstützen Hardware-assisted AddressSanitizer (HWASan), ein ähnliches Tool mit geringerem RAM-Aufwand und einer größeren Bandbreite an erkannten Fehlern. HWASan erkennt zusätzlich zu den von ASan erkannten Fehlern auch die Stack-Nutzung nach der Rückgabe.
HWASan hat einen ähnlichen CPU- und Code-Overhead, aber einen viel geringeren RAM-Overhead (15%). HWASan ist nicht deterministisch. Es gibt nur 256 mögliche Tag-Werte, daher besteht eine konstante Wahrscheinlichkeit von 0,4 %, dass ein Fehler nicht erkannt wird. HWASan hat nicht die ASan-Red Zones mit begrenzter Größe 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 die Freigabe des Arbeitsspeichers her ist. Das macht HWASan besser als ASan. Weitere Informationen zum Design von HWASan oder zur Verwendung von HWASan unter Android
ASan erkennt neben Heap-Überläufen auch Stack- und globale Überläufe und ist schnell mit minimalem Speicher-Overhead.
In diesem Dokument wird beschrieben, wie Sie Teile oder die gesamte Android-Plattform 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 den Code nach vorhandenen Beispielen durchsuchen oder die anderen verfügbaren Sanitizer finden.
Wenn ein Fehler erkannt wird, gibt ASan einen ausführlichen Bericht sowohl in der Standardausgabe als auch in logcat aus und lässt dann den Prozess 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 besteht darin, Android.mk für das betreffende Modul Folgendes 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 den entsprechenden Abschnitt von /init.rc oder /init.$device$.rc hinzu.
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 möglicherweise SELinux deaktivieren:
adb rootadb 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, framepointerbasierten Unwinder, um einen Stacktrace für jedes Arbeitsspeicherzuweisungs- und -freigabeereignis im Programm aufzuzeichnen. Der Großteil von Android wird ohne Framepointer erstellt. Daher erhalten Sie oft nur ein oder zwei aussagekräftige Frames. Um dieses Problem zu beheben, erstellen Sie die Bibliothek entweder mit ASan neu (empfohlen!) oder mit:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Alternativ können Sie ASAN_OPTIONS=fast_unwind_on_malloc=0 in der Prozessumgebung festlegen. Letzteres kann je nach Last sehr CPU-intensiv sein.
Symbolisierung
Anfangs enthalten ASan-Berichte Verweise auf Offsets in Binärdateien und gemeinsam genutzten Bibliotheken. Es gibt zwei Möglichkeiten, Informationen zu Quelldatei und Zeile zu erhalten:
- Prüfen Sie, ob die Binärdatei
llvm-symbolizerin/system/binvorhanden ist.llvm-symbolizerwird aus Quellen inthird_party/llvm/tools/llvm-symbolizererstellt. - Filtern Sie den Bericht mit dem Skript
external/compiler-rt/lib/asan/scripts/symbolize.py.
Der zweite Ansatz kann aufgrund der Verfügbarkeit symbolisierter Bibliotheken auf dem Host mehr Daten (d. h. file:line-Standorte) liefern.
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, die in
diesem Fall /system/bin/app_process(32|64)ist. Dadurch wird ASan gleichzeitig in allen Apps auf dem Gerät aktiviert, was eine hohe Last bedeutet. Ein Gerät mit 2 GB RAM sollte das aber bewältigen können.
Fügen Sie LOCAL_SANITIZE:=address der
Build-Regel in frameworks/base/cmds/app_process hinzu.app_process
Bearbeiten Sie den service zygote Abschnitt der
entsprechenden Datei system/core/rootdir/init.zygote(32|64).rc und fügen Sie dem Block mit eingerückten Zeilen, der class main enthält, die folgenden Zeilen hinzu. Die
Einrückung muss dabei gleich bleiben:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
setenv ASAN_OPTIONS allow_user_segv_handler=true
Erstellen Sie die Plattform, führen Sie „adb sync“ und „fastboot flash boot“ aus und starten Sie das Gerät neu.
Die Wrap-Property verwenden
Mit dem Ansatz im vorherigen Abschnitt wird ASan in jeder App im System platziert (genauer gesagt in jedem Nachfolger des Zygote-Prozesses). Es ist möglich, nur eine (oder mehrere) Apps mit ASan auszuführen. Dabei wird etwas mehr Speicher benötigt, die App startet aber langsamer.
Dazu starten Sie Ihre App mit der Property wrap..
Im folgenden Beispiel wird die Gmail App unter ASan ausgeführt:
adb rootadb shell setenforce 0 # disable SELinuxadb 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 Suchpfads für dynamische Bibliotheken 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 unterstützt das gleichzeitige Erstellen der gesamten Android-Plattform mit ASan. Wenn Sie ein Release höher als Android 9 erstellen, ist HWASan eine bessere Wahl.
Führen Sie die folgenden Befehle im selben Build-Baum aus.
make -j42SANITIZE_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: normale in /system/lib (der erste Aufruf von „make“) und ASan-instrumentierte in /data/asan/lib (der zweite Aufruf von „make“). Ausführbare Dateien aus dem zweiten Build überschreiben die aus dem ersten Build. ASan-instrumentierte ausführbare Dateien erhalten einen anderen Suchpfad für Bibliotheken, der /data/asan/lib vor /system/lib enthält, indem /system/bin/linker_asan in PT_INTERP verwendet wird.
Das Build-System löscht die Verzeichnisse mit den Zwischenobjekten, wenn sich der Wert $SANITIZE_TARGET geändert hat. Dadurch wird ein Neuaufbau aller Ziele erzwungen, während die installierten Binärdateien unter /system/lib beibehalten werden.
Einige Ziele können nicht mit ASan erstellt werden:
- Statisch verknüpfte ausführbare Dateien
- Ziele mit
LOCAL_CLANG:=false - Ziele mit
LOCAL_SANITIZE:=falsewerden fürSANITIZE_TARGET=addressnicht mit ASan bereinigt.
Ausführbare Dateien wie diese werden im SANITIZE_TARGET-Build übersprungen und die Version aus dem ersten Aufruf von „make“ bleibt in /system/bin.
Bibliotheken wie diese werden ohne ASan erstellt. Sie können ASan-Code aus den statischen Bibliotheken enthalten, von denen sie abhängen.