UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) sucht während der Kompilierung nach verschiedenen Arten von undefiniertem Verhalten. UBSan kann viele Fehler durch undefiniertes Verhalten erkennen, Android unterstützt jedoch:

  • Ausrichtung
  • bool
  • Grenzwerte
  • enum
  • float-cast-overflow
  • float-divide-by-zero
  • integer-divide-by-zero
  • nonnull-Attribut
  • null
  • Hin- und Rückflug
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • signed-integer-overflow
  • nicht erreichbar
  • unsigned-integer-overflow
  • VLA-gebunden

Der Sanitizer enthält unsigned-integer-overflow, das zwar nicht technisch undefiniertes Verhalten ist, aber in vielen Android-Modulen verwendet wird, einschließlich der Mediaserverkomponenten, um alle latenten Integer-Overflow-Schwachstellen zu beseitigen.

Implementierung

Im Android-Build-System können Sie UBSan global oder lokal aktivieren. Wenn Sie UBSan global aktivieren möchten, legen Sie SANITIZE_TARGET in Android.mk fest. Wenn Sie UBSan auf Modulebene aktivieren möchten, legen Sie LOCAL_SANITIZE fest und geben Sie die undefinierten Verhaltensweisen an, nach denen Sie in Android.mk suchen möchten. Beispiel:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

Und die entsprechende Blueprint-Konfiguration (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

UBSan-Tastenkombinationen

Android bietet außerdem zwei Shortcuts, integer und default-ub, mit denen Sie mehrere Bereinigungsfunktionen gleichzeitig aktivieren können. Mit „integer“ werden integer-divide-by-zero, signed-integer-overflow und unsigned-integer-overflow aktiviert. default-ub aktiviert die Prüfungen, die nur minimale Compilerleistungsprobleme verursachen: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound. Die Integer-Sanitizer-Klasse kann mit SANITIZE_TARGET und LOCAL_SANITIZE verwendet werden, während default-ub nur mit SANITIZE_TARGET verwendet werden kann.

Bessere Fehlerberichte

Bei der standardmäßigen UBSan-Implementierung von Android wird eine bestimmte Funktion aufgerufen, wenn undefiniertes Verhalten auftritt. Standardmäßig ist diese Funktion „abort“. Seit Oktober 2016 gibt es für UBSan unter Android jedoch eine optionale Laufzeitbibliothek, die detailliertere Fehlerberichte liefert, einschließlich des Typs des aufgetretenen undefinierten Verhaltens sowie Informationen zu Datei und Quellcodezeile. Fügen Sie einer Android.mk-Datei Folgendes hinzu, um die Fehlerberichterstattung mit Ganzzahlprüfungen zu aktivieren:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Der Wert LOCAL_SANITIZE aktiviert den Sanitizer während des Builds. Mit LOCAL_SANITIZE_DIAG wird der Diagnosemodus für den angegebenen Sanitizer aktiviert. Es ist möglich, LOCAL_SANITIZE und LOCAL_SANITIZE_DIAG auf unterschiedliche Werte zu setzen, aber nur die Prüfungen in LOCAL_SANITIZE sind aktiviert. Wenn eine Prüfung nicht in LOCAL_SANITIZE, aber in LOCAL_SANITIZE_DIAG angegeben ist, wird sie nicht aktiviert und es werden keine Diagnosemeldungen ausgegeben.

Hier ist ein Beispiel für die Informationen, die von der UBSan-Laufzeitbibliothek bereitgestellt werden:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Bereinigung von Ganzzahlüberläufen

Unbeabsichtigte Ganzzahlüberläufe können zu Speicherbeschädigungen oder Sicherheitslücken für die Offenlegung von Informationen in Variablen führen, die mit Speicherzugriffen oder Speicherzuweisungen verknüpft sind. Um dem entgegenzuwirken, haben wir die Sanitizer für Überläufe von signierten und nicht signierten Ganzzahlen von Clang UndefinedBehaviorSanitizer (UBSan) in das Media-Framework in Android 7.0 aufgenommen. In Android 9 haben wir UBSan auf weitere Komponenten ausgeweitet und die Unterstützung des Build-Systems dafür verbessert.

Damit werden Prüfungen für arithmetische Operationen und Anweisungen hinzugefügt, die überlaufen könnten. So wird ein Prozess sicher abgebrochen, wenn ein Überlauf auftritt. Diese Bereinigungsfunktionen können eine ganze Klasse von Sicherheitslücken in Bezug auf Speicherbeschädigung und Offenlegung von Informationen beheben, bei denen die Ursache ein Integer-Überlauf ist, z. B. die ursprüngliche Stagefright-Sicherheitslücke.

Beispiele und Quelle

Integer Overflow Sanitization (IntSan) wird vom Compiler bereitgestellt und während der Kompilierung in die Binärdatei eingefügt, um arithmetische Überläufe zu erkennen. Sie ist standardmäßig in verschiedenen Komponenten der Plattform aktiviert, z. B. in /platform/external/libnl/Android.bp.

Implementierung

IntSan verwendet die UBSan-Sanitizer für Überläufe von vorzeichenbehafteten und vorzeichenlosen Ganzzahlen. Diese Maßnahme wird auf Modulebene aktiviert. Es trägt zum Schutz wichtiger Android-Komponenten bei und sollte nicht deaktiviert werden.

Wir empfehlen dringend, die Bereinigung von Ganzzahlüberläufen für zusätzliche Komponenten zu aktivieren. Ideale Kandidaten sind privilegierter nativer Code oder nativer Code, der nicht vertrauenswürdige Nutzereingaben parst. Der Sanitizer verursacht einen geringen Leistungsmehraufwand, der von der Verwendung des Codes und der Häufigkeit arithmetischer Operationen abhängt. Rechnen Sie mit einem geringen Overhead-Prozentsatz und testen Sie, ob die Leistung ein Problem darstellt.

IntSan in Makefiles unterstützen

So aktivieren Sie IntSan in einem Makefile:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE akzeptiert eine durch Kommas getrennte Liste von Bereinigern. integer_overflow ist ein vorkonfiguriertes Set von Optionen für die einzelnen Bereiniger für Überläufe von vorzeichenbehafteten und vorzeichenlosen Ganzzahlen mit einer Standard-BLOCKLISTE.
  • Mit LOCAL_SANITIZE_DIAG wird der Diagnosemodus für die Bereinigungsfunktionen aktiviert. Verwenden Sie den Diagnosemodus nur während des Tests, da er bei Überläufen nicht abgebrochen wird. Dadurch wird der Sicherheitsvorteil der Maßnahme vollständig zunichte gemacht. Weitere Informationen finden Sie unter Fehlerbehebung.
  • Mit LOCAL_SANITIZE_BLOCKLIST können Sie eine BLOCKLIST-Datei angeben, um zu verhindern, dass Funktionen und Quelldateien bereinigt werden. Weitere Informationen finden Sie unter Fehlerbehebung.

Wenn Sie eine genauere Steuerung wünschen, aktivieren Sie die Bereinigungsfunktionen einzeln mit einem oder beiden Flags:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Unterstützung von IntSan in Blueprint-Dateien

Um die Bereinigung von Ganzzahlüberläufen in einer Blueprint-Datei wie /platform/external/libnl/Android.bp zu aktivieren, fügen Sie Folgendes hinzu:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Wie bei Makefiles ist das Attribut integer_overflow ein vorgefertigtes Set von Optionen für die einzelnen Sanitizer für Überläufe von vorzeichenbehafteten und vorzeichenlosen Ganzzahlen mit einer Standard-BLOCKLISTE.

Mit der Gruppe von diag-Properties wird der Diagnosemodus für die Bereinigungsfunktionen aktiviert. Verwenden Sie den Diagnosemodus nur während des Testens. Im Diagnosemodus wird bei Überläufen nicht abgebrochen, was den Sicherheitsvorteil der Mitigation in Nutzer-Builds zunichte macht. Weitere Informationen finden Sie unter Fehlerbehebung.

Mit dem Attribut BLOCKLIST kann eine BLOCKLIST-Datei angegeben werden, mit der Entwickler verhindern können, dass Funktionen und Quelldateien bereinigt werden. Weitere Informationen finden Sie unter Fehlerbehebung.

So aktivieren Sie die Bereinigungsfunktionen einzeln:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Fehlerbehebung

Wenn Sie die Bereinigung von Ganzzahlüberläufen in neuen Komponenten aktivieren oder auf Plattformbibliotheken angewiesen sind, die die Bereinigung von Ganzzahlüberläufen enthalten, kann es zu Problemen mit harmlosen Ganzzahlüberläufen kommen, die zu Abbrüchen führen. Sie sollten Komponenten mit aktivierter Bereinigung testen, um sicherzustellen, dass gutartige Überläufe erkannt werden.

Wenn Sie nach Abbrüchen suchen möchten, die durch die Bereinigung in Nutzer-Builds verursacht wurden, suchen Sie nach SIGABRT-Abstürzen mit Abbruchmeldungen, die auf einen von UBSan abgefangenen Überlauf hinweisen, z. B.:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

Der Stacktrace sollte die Funktion enthalten, die den Abbruch verursacht hat. Überläufe, die in Inlinefunktionen auftreten, sind jedoch möglicherweise nicht im Stacktrace enthalten.

Um die Ursache des Fehlers leichter zu ermitteln, aktivieren Sie die Diagnose in der Bibliothek, die den Abbruch auslöst, und versuchen Sie, den Fehler zu reproduzieren. Wenn die Diagnose aktiviert ist, wird der Prozess nicht abgebrochen, sondern läuft weiter. Wenn Sie nicht abbrechen, können Sie die Anzahl der harmlosen Überläufe in einem bestimmten Ausführungspfad maximieren, ohne nach dem Beheben jedes Fehlers neu kompilieren zu müssen. Bei der Diagnose wird eine Fehlermeldung ausgegeben, die die Zeilennummer und die Quelldatei enthält, die den Abbruch verursacht hat:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Sobald die problematische arithmetische Operation gefunden wurde, muss sichergestellt werden, dass der Überlauf harmlos und beabsichtigt ist (z.B. keine Sicherheitsrisiken birgt). So können Sie das Abbrechen des Bereinigers beheben:

  • Code umgestalten, um den Überlauf zu vermeiden (Beispiel)
  • Überlauf explizit über die Clang-Funktionen __builtin_*_overflow (Beispiel)
  • Deaktivieren der Bereinigung in der Funktion durch Angabe des Attributs no_sanitize (Beispiel)
  • Bereinigung einer Funktion oder Quelldatei über eine BLOCKLIST-Datei deaktivieren (Beispiel)

Sie sollten die detaillierteste Lösung verwenden, die möglich ist. Bei einer großen Funktion mit vielen arithmetischen Operationen und einer einzelnen Operation, die einen Überlauf verursacht, sollte beispielsweise nur die einzelne Operation umgestaltet werden, anstatt die gesamte Funktion auf die BLOCKLIST zu setzen.

Häufige Muster, die zu harmlosen Überläufen führen können:

  • Implizite Umwandlungen, bei denen ein nicht signierter Überlauf auftritt, bevor in einen signierten Typ umgewandelt wird (Beispiel)
  • Löschvorgänge in verknüpften Listen, bei denen der Schleifenindex beim Löschen dekrementiert wird (Beispiel)
  • Einer nicht signierten Variable wird -1 zugewiesen, anstatt den tatsächlichen Maximalwert anzugeben (Beispiel).
  • Schleifen, die eine vorzeichenlose Ganzzahl in der Bedingung dekrementieren (Beispiel, Beispiel)

Entwickler sollten sicherstellen, dass die vom Sanitizer erkannten Überläufe tatsächlich harmlos sind und keine unbeabsichtigten Nebenwirkungen oder Sicherheitsrisiken mit sich bringen, bevor sie die Bereinigung deaktivieren.

IntSan deaktivieren

Sie können IntSan mit BLOCKLISTs oder Funktionsattributen deaktivieren. Deaktivieren Sie sie nur sparsam und nur, wenn das Refaktorieren des Codes ansonsten unangemessen ist oder wenn es problematische Leistungseinbußen gibt.

Weitere Informationen zum Deaktivieren von IntSan mit Funktionsattributen und zur Formatierung von BLOCKLIST-Dateien finden Sie in der Upstream-Clang-Dokumentation. BLOCKLISTing sollte auf den jeweiligen Sanitizer beschränkt werden, indem Abschnittsnamen verwendet werden, die den Ziel-Sanitizer angeben, um Auswirkungen auf andere Sanitizer zu vermeiden.

Zertifizierungsstufe

Derzeit gibt es keinen CTS-Test speziell für die Bereinigung von Integer-Überläufen. Achten Sie stattdessen darauf, dass die CTS-Tests mit oder ohne aktiviertes IntSan bestanden werden, um zu prüfen, ob es sich auf das Gerät auswirkt.

Bereichsbereinigung

BoundsSanitizer (BoundSan) fügt Binärdateien Instrumentierung hinzu, um Bereichsprüfungen bei Arrayzugriffen zu ermöglichen. Diese Prüfungen werden hinzugefügt, wenn der Compiler zur Kompilierzeit nicht nachweisen kann, dass der Zugriff sicher ist, und wenn die Größe des Arrays zur Laufzeit bekannt ist, sodass sie geprüft werden kann. In Android 10 wird BoundSan in Bluetooth und Codecs eingesetzt. BoundSan wird vom Compiler bereitgestellt und ist standardmäßig in verschiedenen Komponenten auf der gesamten Plattform aktiviert.

Implementierung

BoundSan verwendet den UBSan-Bounds-Sanitizer. Diese Maßnahme wird auf Modulebene aktiviert. Sie trägt dazu bei, wichtige Komponenten von Android zu schützen, und sollte nicht deaktiviert werden.

Wir empfehlen dringend, BoundSan für zusätzliche Komponenten zu aktivieren. Ideale Kandidaten sind privilegierter nativer Code oder komplexer nativer Code, der nicht vertrauenswürdige Nutzereingaben parst. Der Leistungs-Overhead, der mit der Aktivierung von BoundSan verbunden ist, hängt von der Anzahl der Arrayzugriffe ab, die nicht als sicher nachgewiesen werden können. Im Durchschnitt ist mit einem geringen Overhead-Prozentsatz zu rechnen. Testen Sie, ob die Leistung ein Problem darstellt.

BoundSan in Blueprint-Dateien aktivieren

BoundSan kann in Blueprint-Dateien aktiviert werden, indem Sie "bounds" dem Attribut misc_undefined für Binär- und Bibliotheksmodule hinzufügen:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

Mit dem Attribut diag wird der Diagnosemodus für die Bereinigungsfunktionen aktiviert. Verwenden Sie den Diagnosemodus nur während des Testens. Im Diagnosemodus wird bei Überläufen nicht abgebrochen, was den Sicherheitsvorteil der Maßnahme zunichte macht und einen höheren Leistungsaufwand mit sich bringt. Daher wird er für Produktions-Builds nicht empfohlen.

SPERRLISTE

Mit dem Attribut BLOCKLIST kann eine BLOCKLIST-Datei angegeben werden, mit der Entwickler verhindern können, dass Funktionen und Quelldateien bereinigt werden. Verwenden Sie diese Eigenschaft nur, wenn die Leistung ein Problem darstellt und die betreffenden Dateien/Funktionen einen wesentlichen Beitrag leisten. Prüfen Sie diese Dateien/Funktionen manuell, um sicherzustellen, dass Arrayzugriffe sicher sind. Weitere Informationen finden Sie unter Fehlerbehebung.

BoundSan in Makefiles aktivieren

BoundSan kann in Makefiles aktiviert werden, indem Sie "bounds" zur Variablen LOCAL_SANITIZE für Binär- und Bibliotheksmodule hinzufügen:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE akzeptiert eine durch Kommas getrennte Liste von Bereinigungsfunktionen.

LOCAL_SANITIZE_DIAG aktiviert den Diagnosemodus. Verwenden Sie den Diagnosemodus nur während des Testens. Im Diagnosemodus wird bei Überläufen nicht abgebrochen. Dadurch wird der Sicherheitsvorteil der Maßnahme zunichte gemacht und es entsteht ein höherer Leistungsaufwand. Daher wird er nicht für Produktions-Builds empfohlen.

Mit LOCAL_SANITIZE_BLOCKLIST kann eine BLOCKLIST-Datei angegeben werden, mit der Entwickler verhindern können, dass Funktionen und Quelldateien bereinigt werden. Verwenden Sie diese Eigenschaft nur, wenn die Leistung ein Problem darstellt und die betreffenden Dateien/Funktionen einen wesentlichen Beitrag leisten. Prüfen Sie diese Dateien/Funktionen manuell, um sicherzustellen, dass Arrayzugriffe sicher sind. Weitere Informationen finden Sie unter Fehlerbehebung.

BoundSan deaktivieren

Sie können BoundSan in Funktionen und Quelldateien mit BLOCKLISTs oder Funktionsattributen deaktivieren. Es ist am besten, BoundSan aktiviert zu lassen. Deaktivieren Sie es nur, wenn die Funktion oder Datei einen hohen Leistungsaufwand verursacht und der Quellcode manuell überprüft wurde.

Weitere Informationen zum Deaktivieren von BoundSan mit Funktionsattributen und zur Formatierung der BLOCKLIST-Datei finden Sie in der Clang/LLVM-Dokumentation. Beschränken Sie die BLOCKLIST auf den jeweiligen Sanitizer, indem Sie Abschnittsnamen verwenden, die den Ziel-Sanitizer angeben, um Auswirkungen auf andere Sanitizer zu vermeiden.

Zertifizierungsstufe

Es gibt keine CTS-Tests speziell für BoundSan. Stellen Sie stattdessen sicher, dass die CTS-Tests mit oder ohne aktiviertes BoundSan bestanden werden, um zu prüfen, ob das Gerät beeinträchtigt wird.

Fehlerbehebung

Testen Sie Komponenten nach dem Aktivieren von BoundSan gründlich, um sicherzustellen, dass alle zuvor nicht erkannten Zugriffe außerhalb des zulässigen Bereichs behoben werden.

BoundSan-Fehler lassen sich leicht erkennen, da sie die folgende Tombstone-Abbruchmeldung enthalten:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Wenn der Diagnosemodus ausgeführt wird, werden die Quelldatei, die Zeilennummer und der Indexwert in logcat ausgegeben. Standardmäßig wird in diesem Modus keine Abbruchmeldung ausgegeben. Prüfen Sie logcat auf Fehler.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'