AddressSanitizer

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 in third_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 Ziele
  • LOCAL_SANITIZE:=false sind nicht ASan-kompatibel für SANITIZE_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.

Zusätzliche Dokumentation