AddressSanitizer (ASan) ist ein schnelles Compiler-basiertes Tool zum Erkennen von Speicherfehlern in nativem Code.
ASan erkennt:
- Stapel- und Heap-Pufferüberlauf/-unterlauf
- Heap-Nutzung nach dem kostenlosen
- Stack-Nutzung außerhalb des Geltungsbereichs
- 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 Zweifache, der Codegrößen-Overhead liegt zwischen 50 % und dem Zweifachen und es gibt einen großen Speicher-Overhead (abhängig von Ihren Zuordnungsmustern, aber in der Größenordnung von 2x).
Android 10 und der AOSP-Hauptzweig auf AArch64 unterstützen hardwarebeschleunigtes ASan (HWASan) , ein ähnliches Tool mit geringerem RAM-Overhead und einer größeren Auswahl erkannter Fehler. HWASan erkennt zusätzlich zu den von ASan erkannten Fehlern die Stack-Nutzung nach der Rückkehr.
HWASan hat einen ähnlichen CPU- und Codegrößen-Overhead, aber einen viel geringeren RAM-Overhead (15 %). HWASan ist nichtdeterministisch. Es gibt nur 256 mögliche Tag-Werte, sodass die Wahrscheinlichkeit, dass ein Fehler übersehen wird, pauschal bei 0,4 % liegt. HWASan verfügt nicht über die roten 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“, sodass es für HWASan keine Rolle spielt, wie groß der Überlauf ist oder wie lange die Speicherfreigabe zurückliegt. Das macht HWASan besser als ASan. Sie können mehr über das Design von HWASan oder über die Verwendung von HWASan auf Android lesen.
ASan erkennt zusätzlich zu Heap-Überläufen auch Stapel-/globale Überläufe und ist schnell mit minimalem Speicheraufwand.
In diesem Dokument wird beschrieben, wie Sie Teile/das gesamte Android mit ASan erstellen und ausführen. Wenn Sie eine SDK/NDK-App mit ASan erstellen, lesen Sie stattdessen Address Sanitizer .
Bereinigen einzelner ausführbarer Dateien mit ASan
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 in der Standardausgabe als auch in logcat
aus und stürzt dann den Prozess ab.
Bereinigen gemeinsam genutzter Bibliotheken mit ASan
Aufgrund der Funktionsweise von ASan kann eine mit ASan erstellte Bibliothek nur von einer ausführbaren Datei verwendet werden, die mit ASan erstellt wurde.
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. Die empfohlene Vorgehensweise 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
statt in /system/lib
abgelegt. Führen Sie dann Ihre ausführbare Datei aus mit:
LD_LIBRARY_PATH=/system/lib/asan
Fügen Sie für Systemdämonen Folgendes zum entsprechenden Abschnitt von /init.rc
oder /init.$device$.rc
hinzu.
setenv LD_LIBRARY_PATH /system/lib/asan
Stellen Sie sicher, dass der Prozess Bibliotheken aus /system/lib/asan
verwendet, sofern vorhanden, indem Sie /proc/$PID/maps
lesen. Ist dies nicht der Fall, 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 Stapelspuren
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 erstellt. Dadurch erhält man oft nur ein oder zwei aussagekräftige Frames. Um dies zu beheben, erstellen Sie die Bibliothek entweder mit ASan neu (empfohlen!) oder mit:
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 Auslastung sehr rechenintensiv 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 abzurufen:
- Stellen Sie sicher, dass die Binärdatei
llvm-symbolizer
in/system/bin
vorhanden ist.llvm-symbolizer
wird aus Quellenthird_party/llvm/tools/llvm-symbolizer
erstellt. - 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 (d. h. file:line
) bereitstellen.
ASan in Apps
ASan kann keinen Einblick in den Java-Code erhalten, aber es kann 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, aber ein Gerät mit 2 GB RAM sollte damit zurechtkommen.
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 (sofern es zum Zeitpunkt des Lesens noch vorhanden ist).
Bearbeiten Sie den Abschnitt service zygote
der entsprechenden Datei system/core/rootdir/init.zygote( 32|64 ).rc
, um die folgenden Zeilen zum Block eingerückter Zeilen hinzuzufügen, der class main
enthält, ebenfalls um den gleichen Betrag eingerückt:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Erstellen, ADB-Synchronisierung, 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 und dabei etwas Speicheraufwand gegen einen langsameren App-Start einzutauschen.
Dies können Sie erreichen, indem Sie Ihre App mit dem wrap.
Eigentum. 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 Zusammenhang 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 der dynamischen Bibliothek 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
ausgeführt werden.
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 bietet Unterstützung für die gleichzeitige Erstellung der gesamten Android-Plattform mit ASan. (Wenn Sie eine höhere 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 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 diejenigen aus dem ersten Build. Mit ASan instrumentierte ausführbare Dateien erhalten durch die Verwendung von /system/bin/linker_asan
in PT_INTERP
einen anderen Bibliothekssuchpfad, der /data/asan/lib
vor /system/lib
enthält.
Das Build-System blockiert Zwischenobjektverzeichnisse, wenn sich der Wert $SANITIZE_TARGET
geändert hat. Dadurch wird eine Neuerstellung 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
Ziele -
LOCAL_SANITIZE:=false
werden nicht fürSANITIZE_TARGET=address
geprüft
Ausführbare Dateien wie diese werden im SANITIZE_TARGET
-Build übersprungen und die Version aus dem ersten Make-Aufruf verbleibt in /system/bin
.
Bibliotheken wie diese werden ohne ASan erstellt. Sie können ASan-Code aus den statischen Bibliotheken enthalten, von denen sie abhängig sind.