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 außerhalb des Gültigkeitsbereichs verwendet
  • Doppeltes Freibad/Wildtreffer

ASan wird sowohl auf 32-Bit- als auch auf 64-Bit-ARM- sowie auf x86- und x86-64-Systemen ausgeführt. Der CPU-Overhead von ASan ist etwa doppelt so hoch, der Codegrößen-Overhead liegt zwischen 50 % und dem Doppelten und es gibt einen großen Arbeitsspeicher-Overhead (je nach Allokationsmuster, aber in der Größenordnung von 2 x).

Android 10 und der AOSP-Hauptzweig auf AArch64 unterstützen Hardware-assisted AddressSanitizer (HWASan), ein ähnliches Tool mit geringerem RAM-Overhead und einer größeren Bandbreite an erkannten Fehlern. HWASan erkennt die Stacknutzung nach der Rückgabe zusätzlich zu den von ASan erkannten Fehlern.

HWASan hat einen ähnlichen CPU- und Codegrößenaufwand, aber einen viel geringeren RAM-Aufwand (15%). HWASan ist nicht deterministisch. Es gibt nur 256 mögliche Tag-Werte, sodass die Wahrscheinlichkeit, dass ein Fehler übersehen wird, bei 0,4 % liegt. HWASan hat nicht die begrenzten roten Zonen von ASan zum Erkennen von Überläufen und die 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 Arbeitsspeicher deaktiviert wurde. Dadurch ist HWASan besser als ASan. Weitere Informationen zum Design von HWASan oder zur Verwendung von HWASan auf Android-Geräten

ASan erkennt neben Heap-Overflows auch Stack-/globale Overflows und ist schnell mit minimalem Arbeitsspeicheraufwand.

In diesem Dokument wird beschrieben, wie Teile bzw. das gesamte Android-Betriebssystem mit ASan erstellt und ausgeführt wird. Wenn Sie eine SDK-/NDK-App mit ASan erstellen, lesen Sie stattdessen den Hilfeartikel 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 den anderen verfügbaren Sanitizern suchen.

Wenn ein Fehler erkannt wird, druckt ASan sowohl in der Standardausgabe als auch in logcat einen ausführlichen Bericht aus und stürzt 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.

Zum Bereinigen einer gemeinsam genutzten Bibliothek, die in mehreren ausführbaren Dateien verwendet wird und nicht alle mit ASan erstellt wird, benötigen Sie zwei Kopien der Bibliothek. Wir empfehlen, 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 statt in /system/lib abgelegt. Führen Sie dann die ausführbare Datei mit folgendem Befehl aus:

LD_LIBRARY_PATH=/system/lib/asan

Fügen Sie für System-Daemons dem entsprechenden Abschnitt von /init.rc oder /init.$device$.rc Folgendes hinzu:

setenv LD_LIBRARY_PATH /system/lib/asan

Prüfen Sie, ob der Prozess Bibliotheken von /system/lib/asan verwendet, sofern vorhanden. Lesen Sie dazu /proc/$PID/maps. Andernfalls 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, auf dem Frame-Pointer basierenden Unwinder, um für jedes Ereignis der Speicherzuweisung und ‑entfernung im Programm einen Stack-Trace aufzuzeichnen. Die meisten Android-Versionen enthalten keine Frame-Pointer. Infolgedessen erhalten Sie oft nur ein oder zwei aussagekräftige Frames. Um das Problem zu beheben, können Sie die Bibliothek entweder mit ASan (empfohlen) oder mit einem der folgenden Tools neu erstellen:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

Sie können auch ASAN_OPTIONS=fast_unwind_on_malloc=0 in der Prozessumgebung festlegen. Letztere kann je nach Last sehr CPU-intensiv sein.

Symbolisierung

Ursprünglich enthalten ASan-Berichte Verweise auf Abweichungen in Binärdateien und freigegebenen Bibliotheken. Es gibt zwei Möglichkeiten, Informationen zur Quelldatei und zur Zeile abzurufen:

  • Prüfen Sie, ob die llvm-symbolizer-Binärdatei in /system/bin vorhanden ist. llvm-symbolizer wird aus Quellen in third_party/llvm/tools/llvm-symbolizer erstellt.
  • Filtern Sie den Bericht mit dem external/compiler-rt/lib/asan/scripts/symbolize.py-Script.

Der zweite Ansatz kann aufgrund der Verfügbarkeit symbolisierter Bibliotheken auf dem Host mehr Daten (d. h. file:line-Standorte) liefern.

Asana in Apps

ASan kann keinen 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. Das ist eine hohe Belastung, aber ein Gerät mit 2 GB RAM sollte damit umgehen können.

Fügen Sie der Build-Regel app_process in frameworks/base/cmds/app_process LOCAL_SANITIZE:=address hinzu. Ignorieren Sie vorerst das Ziel app_process__asan in derselben Datei, wenn es zum Zeitpunkt des Lesens noch vorhanden ist.

Bearbeiten Sie den Abschnitt service zygote der entsprechenden system/core/rootdir/init.zygote(32|64).rc-Datei, um dem Block mit den eingerückten Zeilen, die class main enthalten, die folgenden Zeilen hinzuzufügen, die ebenfalls um denselben Betrag eingerückt sind:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

Build, ADB-Synchronisierung, Fastboot Flash Boot und Neustart.

Das Attribut „umbruch“ verwenden

Mit dem Ansatz im vorherigen Abschnitt wird ASan in jede Anwendung im System eingefügt (tatsächlich in jeden Nachfolger des Zygote-Prozesses). Es ist möglich, nur eine (oder mehrere) Apps mit ASan auszuführen und einen gewissen Speicheroverhead für ein langsameres App-Starten in Kauf zu nehmen.

Sie können dazu die Property wrap. verwenden. 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 wird. Außerdem wird /system/lib/asan an den Anfang des Suchpfads der dynamischen Bibliothek eingefügt. So werden ASan-instrumentierte Bibliotheken aus /system/lib/asan bei der Ausführung mit asanwrapper normalen Bibliotheken in /system/lib vorgezogen.

Wenn ein Fehler gefunden wird, stürzt die App ab und der Bericht wird in das Protokoll gedruckt.

SANITIZE_TARGET

Android 7.0 und höher unterstützen das Erstellen der gesamten Android-Plattform mit ASan auf einmal. Wenn Sie eine Version entwickeln, die höher als Android 9 ist, 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 gemeinsam genutzte Bibliotheken erstellt: eine normale in /system/lib (die erste make-Aufruf) und eine mit ASan instrumentierte in /data/asan/lib (die 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 enthält. Dazu wird /system/bin/linker_asan in PT_INTERP verwendet.

Das Build-System überschreibt Zwischenobjektverzeichnisse, wenn sich der Wert von $SANITIZE_TARGET geändert hat. Dadurch wird ein Neuaufbau aller Ziele erzwungen, während die installierten Binärdateien unter /system/lib erhalten bleiben.

Einige Ziele können nicht mit ASan erstellt werden:

  • Statisch verknüpfte ausführbare Dateien
  • LOCAL_CLANG:=false Zielvorhaben
  • LOCAL_SANITIZE:=false sind nicht für SANITIZE_TARGET=address geeignet

Solche ausführbaren Dateien werden im SANITIZE_TARGET-Build übersprungen und die Version aus dem ersten „make“-Aufruf bleibt in /system/bin.

Solche Bibliotheken werden ohne ASan erstellt. Sie können ASan-Code aus den statischen Bibliotheken enthalten, von denen sie abhängig sind.

Zusätzliche Dokumentation