Integrität des Kontrollflusses

Ab 2016 sind etwa 86% aller Sicherheitslücken in Android mit der Speichersicherheit verbunden. Die meisten Sicherheitslücken werden von Angreifern ausgenutzt, die den normalen Kontrollfluss einer Anwendung ändern, um beliebige böswillige Aktivitäten mit allen Berechtigungen der ausgenutzten Anwendung auszuführen. Control Flow Integrity (CFI) ist ein Sicherheitsmechanismus, der Änderungen am ursprünglichen Kontrollflussdiagramm einer kompilierten Binärdatei nicht zulässt, wodurch die Durchführung solcher Angriffe erheblich erschwert wird.

In Android 8.1 haben wir die Implementierung von CFI durch LLVM im Medienstapel aktiviert. In Android 9 haben wir CFI in mehr Komponenten und auch im Kernel aktiviert. Die System-CFI ist standardmäßig aktiviert, Sie müssen jedoch die Kernel-CFI aktivieren.

Das CFI von LLVM muss mit Link-Time Optimization (LTO) kompiliert werden. LTO behält die LLVM-Bitcodedarstellung von Objektdateien bis zur Verknüpfungszeit bei, wodurch der Compiler besser überlegen kann, welche Optimierungen durchgeführt werden können. Durch Aktivieren von LTO wird die Größe der endgültigen Binärdatei verringert und die Leistung verbessert, die Kompilierungszeit wird jedoch erhöht. Beim Testen auf Android führt die Kombination von LTO und CFI zu einem vernachlässigbaren Overhead für die Codegröße und -leistung. in einigen Fällen verbesserten sich beide.

Weitere technische Details zu CFI und zum Umgang mit anderen Vorwärtskontrollprüfungen finden Sie in der LLVM-Konstruktionsdokumentation .

Beispiele und Quelle

CFI wird vom Compiler bereitgestellt und fügt der Binärdatei während der Kompilierungszeit Instrumente hinzu. Wir unterstützen CFI in der Clang-Toolchain und das Android-Build-System in AOSP.

CFI ist standardmäßig für Arm64-Geräte für den Komponentensatz in /platform/build/target/product/cfi-common.mk . Es ist auch direkt in den Makefiles / Blueprint-Dateien einer Reihe von Medienkomponenten aktiviert, z. B. /platform/frameworks/av/media/libmedia/Android.bp und /platform/frameworks/av/cmds/stagefright/Android.mk .

Implementierung des System-CFI

CFI ist standardmäßig aktiviert, wenn Sie Clang und das Android-Build-System verwenden. Da CFI die Sicherheit von Android-Benutzern gewährleistet, sollten Sie es nicht deaktivieren.

Wir empfehlen Ihnen dringend, CFI für zusätzliche Komponenten zu aktivieren. Ideale Kandidaten sind privilegierter nativer Code oder nativer Code, der nicht vertrauenswürdige Benutzereingaben verarbeitet. Wenn Sie clang und das Android-Build-System verwenden, können Sie CFI in neuen Komponenten aktivieren, indem Sie Ihren Makefiles oder Blueprint-Dateien einige Zeilen hinzufügen.

Unterstützung von CFI in Makefiles

/platform/frameworks/av/cmds/stagefright/Android.mk Sie Folgendes /platform/frameworks/av/cmds/stagefright/Android.mk , um CFI in einer make-Datei wie /platform/frameworks/av/cmds/stagefright/Android.mk :

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE gibt CFI als Desinfektionsmittel während des LOCAL_SANITIZE an.
  • LOCAL_SANITIZE_DIAG Diagnosemodus für CFI. Der Diagnosemodus druckt während eines Absturzes zusätzliche Debug-Informationen in logcat aus. Dies ist hilfreich beim Entwickeln und Testen Ihrer Builds. Stellen Sie jedoch sicher, dass Sie den Diagnosemodus bei Produktionen entfernen.
  • LOCAL_SANITIZE_BLACKLIST können Komponenten die CFI-Instrumentierung für einzelne Funktionen oder Quelldateien selektiv deaktivieren. Sie können eine schwarze Liste als letzten Ausweg verwenden, um Probleme mit dem Benutzer zu beheben, die andernfalls auftreten könnten. Weitere Informationen finden Sie unter Deaktivieren von CFI .

Unterstützung von CFI in Blueprint-Dateien

/platform/frameworks/av/media/libmedia/Android.bp , um CFI in einer Blueprint-Datei wie /platform/frameworks/av/media/libmedia/Android.bp :

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

Fehlerbehebung

Wenn Sie CFI in neuen Komponenten aktivieren, können einige Probleme mit Fehlanpassungsfehlern des Funktionstyps und Fehlanpassungsfehlern des Assembler-Codetyps auftreten .

Fehlanpassungsfehler beim Funktionstyp treten auf, weil CFI indirekte Aufrufe so einschränkt, dass nur zu Funktionen gesprungen wird, die denselben dynamischen Typ wie der im Aufruf verwendete statische Typ haben. CFI beschränkt virtuelle und nicht virtuelle Elementfunktionsaufrufe darauf, nur zu Objekten zu springen, die eine abgeleitete Klasse des statischen Typs des Objekts sind, das zum Aufrufen verwendet wird. Dies bedeutet, wenn Sie Code haben, der gegen eine dieser Annahmen verstößt, wird die von CFI hinzugefügte Instrumentierung abgebrochen. Beispielsweise zeigt die Stapelverfolgung ein SIGABRT und logcat enthält eine Zeile über die Integrität des Kontrollflusses, in der eine Nichtübereinstimmung festgestellt wird.

Um dies zu beheben, stellen Sie sicher, dass die aufgerufene Funktion denselben Typ hat, der statisch deklariert wurde. Hier sind zwei Beispiel-CLs:

Ein weiteres mögliches Problem besteht darin, CFI in Code zu aktivieren, der indirekte Aufrufe der Assembly enthält. Da der Assemblycode nicht eingegeben wird, führt dies zu einer Typinkongruenz.

Um dies zu beheben, erstellen Sie native Code-Wrapper für jeden Assembly-Aufruf und geben Sie den Wrappern dieselbe Funktionssignatur wie dem aufrufenden Zeiger. Der Wrapper kann dann den Assemblycode direkt aufrufen. Da direkte Zweigstellen nicht von CFI instrumentiert werden (sie können zur Laufzeit nicht neu terminiert werden und stellen daher kein Sicherheitsrisiko dar), wird das Problem behoben.

Wenn zu viele Assembly-Funktionen vorhanden sind und nicht alle behoben werden können, können Sie auch alle Funktionen auf die schwarze Liste setzen, die indirekte Assembly-Aufrufe enthalten. Dies wird nicht empfohlen, da dadurch die CFI-Überprüfungen dieser Funktionen deaktiviert werden und die Angriffsfläche geöffnet wird.

CFI deaktivieren

Wir haben keinen Leistungsaufwand festgestellt, daher sollten Sie CFI nicht deaktivieren müssen. Wenn es jedoch zu einer Auswirkung auf den Benutzer kommt, können Sie CFI für einzelne Funktionen oder Quelldateien selektiv deaktivieren, indem Sie zur Kompilierungszeit eine Desinfektions-Blacklist-Datei bereitstellen. Die Blacklist weist den Compiler an, die CFI-Instrumentierung an bestimmten Stellen zu deaktivieren.

Das Android-Build-System bietet Unterstützung für Blacklists pro Komponente (mit denen Sie Quelldateien oder einzelne Funktionen auswählen können, die keine CFI-Instrumentierung erhalten) für Make und Soong. Weitere Informationen zum Format einer Blacklist-Datei finden Sie in den vorgelagerten Clang-Dokumenten .

Validierung

Derzeit gibt es keinen CTS-Test speziell für CFI. Stellen Sie stattdessen sicher, dass CTS-Tests mit oder ohne aktiviertem CFI bestanden werden, um sicherzustellen, dass CFI keine Auswirkungen auf das Gerät hat.