Arm v9 führt Arm Memory Tagging Extension (MTE) ein, eine Hardware-Implementierung von Tagged Memory.
Auf hoher Ebene kennzeichnet MTE jede Speicherzuweisung/-freigabe mit zusätzlichen Metadaten. Es weist einem Speicherort ein Tag zu, das dann mit Zeigern verknüpft werden kann, die auf diesen Speicherort verweisen. Zur Laufzeit prüft die CPU bei jedem Laden und Speichern, ob der Zeiger und die Metadaten-Tags übereinstimmen.
In Android 12 kann der Kernel- und Userspace-Heap-Speicherzuordner jede Zuweisung mit Metadaten erweitern. Dies hilft dabei, Use-After-Free- und Buffer-Overflow-Fehler zu erkennen, die die häufigste Ursache für Speichersicherheitsfehler in unseren Codebasen sind.
MTE-Betriebsarten
MTE verfügt über drei Betriebsmodi:
- Synchronmodus (SYNC)
- Asynchroner Modus (ASYNC)
- Asymmetrischer Modus (ASYMM)
Synchronmodus (SYNC)
Dieser Modus ist hinsichtlich der Korrektheit der Fehlererkennung gegenüber der Leistung optimiert und kann als präzises Fehlererkennungstool verwendet werden, wenn ein höherer Leistungsaufwand akzeptabel ist. Wenn MTE SYNC aktiviert ist, dient es als Sicherheitsmaßnahme. Bei einer Tag-Nichtübereinstimmung bricht der Prozessor die Ausführung sofort ab und beendet den Prozess mit SIGSEGV
(Code SEGV_MTESERR
) und vollständigen Informationen über den Speicherzugriff und die fehlerhafte Adresse.
Wir empfehlen die Verwendung dieses Modus während des Testens als Alternative zu HWASan/KASAN oder in der Produktion, wenn der Zielprozess eine anfällige Angriffsfläche darstellt. Wenn der ASYNC-Modus außerdem das Vorhandensein eines Fehlers angezeigt hat, kann ein genauer Fehlerbericht erstellt werden, indem die Ausführung mithilfe der Laufzeit-APIs in den SYNC-Modus umgeschaltet wird.
Bei der Ausführung im SYNC-Modus zeichnet der Android-Allocator Stack-Traces für alle Zuweisungen und Freigaben auf und verwendet sie, um bessere Fehlerberichte bereitzustellen, die eine Erklärung eines Speicherfehlers, wie z. B. Use-After-Free oder Buffer-Overflow, und des Stacks enthalten Spuren der relevanten Erinnerungsereignisse. Solche Berichte liefern mehr Kontextinformationen und erleichtern die Verfolgung und Behebung von Fehlern.
Asynchroner Modus (ASYNC)
Dieser Modus ist hinsichtlich der Leistung gegenüber der Genauigkeit von Fehlerberichten optimiert und kann zur Erkennung von Speichersicherheitsfehlern mit geringem Overhead verwendet werden.
Bei einer Tag-Nichtübereinstimmung setzt der Prozessor die Ausführung bis zum nächsten Kernel-Eintrag (z. B. einem Systemaufruf oder einem Timer-Interrupt) fort und beendet dort den Prozess mit SIGSEGV
(Code SEGV_MTEAERR
), ohne die fehlerhafte Adresse oder den Speicherzugriff aufzuzeichnen.
Wir empfehlen, diesen Modus in der Produktion auf gut getesteten Codebasen zu verwenden, bei denen die Dichte von Speichersicherheitsfehlern bekanntermaßen gering ist. Dies wird durch die Verwendung des SYNC-Modus während des Tests erreicht.
Asymmetrischer Modus (ASYMM)
Als zusätzliche Funktion in Arm v8.7-A bietet der asymmetrische MTE-Modus eine synchrone Überprüfung von Speicherlesevorgängen und eine asynchrone Überprüfung von Speicherschreibvorgängen mit einer ähnlichen Leistung wie der ASYNC-Modus. In den meisten Situationen stellt dieser Modus eine Verbesserung gegenüber dem ASYNC-Modus dar und wir empfehlen, ihn anstelle von ASYNC zu verwenden, wann immer er verfügbar ist.
Aus diesem Grund erwähnt keine der unten beschriebenen APIs den asymmetrischen Modus. Stattdessen kann das Betriebssystem so konfiguriert werden, dass es immer den asymmetrischen Modus verwendet, wenn Asynchron angefordert wird. Weitere Informationen finden Sie im Abschnitt „Konfigurieren der CPU-spezifischen bevorzugten MTE-Ebene“.
MTE im Userspace
In den folgenden Abschnitten wird beschrieben, wie MTE für Systemprozesse und Anwendungen aktiviert werden kann. MTE ist standardmäßig deaktiviert, es sei denn, eine der folgenden Optionen ist für einen bestimmten Prozess festgelegt (siehe unten , für welche Komponenten MTE aktiviert ist).
Aktivieren von MTE mithilfe des Build-Systems
Als prozessweite Eigenschaft wird MTE durch die Buildzeiteinstellung der ausführbaren Hauptdatei gesteuert. Mit den folgenden Optionen können Sie diese Einstellung für einzelne ausführbare Dateien oder für ganze Unterverzeichnisse im Quellbaum ändern. Die Einstellung wird für Bibliotheken oder alle Ziele ignoriert, die weder ausführbar noch ein Test sind.
1. Aktivieren von MTE in Android.bp
( Beispiel ) für ein bestimmtes Projekt:
MTE-Modus | Einstellung |
---|---|
Asynchrones MTE | sanitize: { memtag_heap: true, } |
Synchrones MTE | sanitize: { memtag_heap: true, diag: { memtag_heap: true, }, } |
oder in Android.mk:
MTE-Modus | Einstellung |
---|---|
Asynchronous MTE | LOCAL_SANITIZE := memtag_heap |
Synchronous MTE | LOCAL_SANITIZE := memtag_heap LOCAL_SANITIZE_DIAG := memtag_heap |
2. Aktivieren von MTE in einem Unterverzeichnis im Quellbaum mithilfe einer Produktvariablen:
MTE-Modus | Liste einschließen | Ausschlussliste |
---|---|---|
asynchron | PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
synchronisieren | PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
oder
MTE-Modus | Einstellung |
---|---|
Asynchrones MTE | MEMTAG_HEAP_ASYNC_INCLUDE_PATHS |
Synchrones MTE | MEMTAG_HEAP_SYNC_INCLUDE_PATHS |
oder durch Angabe des Ausschlusspfads einer ausführbaren Datei:
MTE-Modus | Einstellung |
---|---|
Asynchrones MTE | PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS |
Synchrones MTE |
Beispiel (ähnliche Verwendung wie PRODUCT_CFI_INCLUDE_PATHS
)
PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor) PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \ vendor/$(vendor)/projectB
Aktivieren von MTE mithilfe von Systemeigenschaften
Die oben genannten Build-Einstellungen können zur Laufzeit überschrieben werden, indem die folgende Systemeigenschaft festgelegt wird:
arm64.memtag.process.<basename> = (off|sync|async)
Dabei steht basename
für den Basisnamen der ausführbaren Datei.
Um beispielsweise /system/bin/ping
oder /data/local/tmp/ping
für die Verwendung von asynchronem MTE festzulegen, verwenden Sie adb shell setprop arm64.memtag.process.ping async
.
Aktivieren von MTE mithilfe einer Umgebungsvariablen
Eine weitere Möglichkeit, die Build-Einstellung zu überschreiben, besteht darin, die Umgebungsvariable zu definieren: MEMTAG_OPTIONS=(off|sync|async)
Wenn sowohl die Umgebungsvariable als auch die Systemeigenschaft definiert sind, hat die Variable Vorrang.
MTE für Anwendungen aktivieren
Wenn nicht angegeben, ist MTE standardmäßig deaktiviert, aber Apps, die MTE verwenden möchten, können dies tun, indem sie android:memtagMode
unter dem Tag <application>
oder <process>
in AndroidManifest.xml
festlegen.
android:memtagMode=(off|default|sync|async)
Wenn das Attribut für das <application>
-Tag festgelegt ist, wirkt es sich auf alle von der Anwendung verwendeten Prozesse aus und kann für einzelne Prozesse durch Festlegen des <process>
-Tags überschrieben werden.
Für Experimente können Kompatibilitätsänderungen verwendet werden, um den Standardwert des memtagMode
Attributs für eine Anwendung festzulegen, die keinen Wert im Manifest angibt (oder default
angibt).
Diese finden Sie unter System > Advanced > Developer options > App Compatibility Changes
im globalen Einstellungsmenü. Durch das Festlegen NATIVE_MEMTAG_ASYNC
oder NATIVE_MEMTAG_SYNC
wird MTE für eine bestimmte Anwendung aktiviert.
Alternativ kann dies mit dem Befehl am
wie folgt eingestellt werden:
$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name
Erstellen eines MTE-System-Images
Wir empfehlen dringend, MTE für alle nativen Binärdateien während der Entwicklung und Bereitstellung zu aktivieren. Dies trägt dazu bei, Speichersicherheitsfehler frühzeitig zu erkennen und bietet eine realistische Benutzerabdeckung, wenn es in Test-Builds aktiviert ist.
Wir empfehlen dringend, MTE während der Entwicklung für alle nativen Binärdateien im synchronen Modus zu aktivieren
SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m
Wie jede Variable im Build-System kann SANITIZE_TARGET
als Umgebungsvariable oder make
-Einstellung verwendet werden (z. B. in einer Datei product.mk
).
Bitte beachten Sie, dass dadurch MTE für alle nativen Prozesse aktiviert wird, jedoch nicht für Anwendungen (die von zygote64
abgeleitet sind), für die MTE gemäß den obigen Anweisungen aktiviert werden kann.
Konfigurieren des CPU-spezifischen bevorzugten MTE-Levels
Auf einigen CPUs kann die Leistung von MTE im ASYMM- oder sogar SYNC-Modus der von ASYNC ähneln. Daher lohnt es sich, strengere Prüfungen für diese CPUs zu aktivieren, wenn ein weniger strenger Prüfmodus angefordert wird, um die Fehlererkennungsvorteile der strengeren Prüfungen ohne Leistungseinbußen zu nutzen.
Standardmäßig werden Prozesse, die für die Ausführung im ASYNC-Modus konfiguriert sind, auf allen CPUs im ASYNC-Modus ausgeführt. Um den Kernel so zu konfigurieren, dass er diese Prozesse im SYNC-Modus auf bestimmten CPUs ausführt, muss der Wert sync beim Booten in den sysfs
Eintrag /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred
geschrieben werden. Dies kann mit einem Init-Skript erfolgen. Um beispielsweise die CPUs 0–1 für die Ausführung von Prozessen im ASYNC-Modus im SYNC-Modus und die CPUs 2–3 für die Ausführung im ASYMM-Modus zu konfigurieren, kann Folgendes zur Init-Klausel eines Hersteller-Init-Skripts hinzugefügt werden:
write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm
Tombstones von Prozessen im ASYNC-Modus, die im SYNC-Modus ausgeführt werden, enthalten eine genaue Stapelverfolgung der Position des Speicherfehlers. Sie enthalten jedoch keinen Stack-Trace für die Zuweisung oder Aufhebung der Zuweisung. Diese Stack-Traces sind nur verfügbar, wenn der Prozess für die Ausführung im SYNC-Modus konfiguriert ist.
int mallopt(M_THREAD_DISABLE_MEM_INIT, level)
wobei level
0 oder 1 ist.
Deaktiviert die Speicherinitialisierung in malloc und vermeidet das Ändern von Speicher-Tags, es sei denn, dies ist aus Gründen der Korrektheit erforderlich.
int mallopt(M_MEMTAG_TUNING, level)
wobei level
ist:
-
M_MEMTAG_TUNING_BUFFER_OVERFLOW
-
M_MEMTAG_TUNING_UAF
Wählt die Tag-Zuweisungsstrategie aus.
- Die Standardeinstellung ist
M_MEMTAG_TUNING_BUFFER_OVERFLOW
. -
M_MEMTAG_TUNING_BUFFER_OVERFLOW
– ermöglicht die deterministische Erkennung von linearen Pufferüberlauf- und -unterlauffehlern, indem benachbarten Zuordnungen unterschiedliche Tag-Werte zugewiesen werden. In diesem Modus ist die Chance, Use-After-Free-Bugs zu erkennen, etwas geringer, da für jeden Speicherort nur die Hälfte der möglichen Tag-Werte verfügbar ist. Bitte beachten Sie, dass MTE keinen Überlauf innerhalb desselben Tag-Granulats (ausgerichteter 16-Byte-Block) erkennen kann und selbst in diesem Modus kleine Überläufe übersehen kann. Ein solcher Überlauf kann nicht die Ursache für eine Speicherbeschädigung sein, da der Speicher innerhalb eines Granulats niemals für mehrere Zuweisungen verwendet wird. -
M_MEMTAG_TUNING_UAF
– ermöglicht unabhängig randomisierte Tags für eine einheitliche Wahrscheinlichkeit von ~93 %, sowohl räumliche (Pufferüberlauf) als auch zeitliche (Verwendung nach freier Nutzung) Fehler zu erkennen.
Zusätzlich zu den oben beschriebenen APIs sollten erfahrene Benutzer möglicherweise Folgendes beachten:
- Durch Festlegen des Hardwareregisters
PSTATE.TCO
kann die Tag-Prüfung vorübergehend unterdrückt werden ( Beispiel ). Zum Beispiel beim Kopieren eines Speicherbereichs mit unbekanntem Tag-Inhalt oder beim Beheben eines Leistungsengpasses in einer Hot-Loop. - Bei Verwendung von
M_HEAP_TAGGING_LEVEL_SYNC
stellt der Systemabsturz-Handler zusätzliche Informationen wie Zuordnungs- und Freigabe-Stack-Traces bereit. Diese Funktionalität erfordert Zugriff auf die Tag-Bits und wird durch Übergabe des FlagsSA_EXPOSE_TAGBITS
beim Festlegen des Signalhandlers aktiviert. Wir empfehlen jedem Programm, das seinen eigenen Signalhandler einrichtet und unbekannte Abstürze an den Systemhandler delegiert, dasselbe zu tun.
MTE im Kernel
Um MTE-beschleunigtes KASAN für den Kernel zu aktivieren, konfigurieren Sie den Kernel mit CONFIG_KASAN=y
, CONFIG_KASAN_HW_TAGS=y
. Diese Konfigurationen sind standardmäßig auf GKI-Kerneln aktiviert, beginnend mit Android 12-5.10
.
Dies kann beim Booten mit den folgenden Befehlszeilenargumenten gesteuert werden:
-
kasan=[on|off]
– KASAN aktivieren oder deaktivieren (Standard:on
) -
kasan.mode=[sync |async ]
– Wählen Sie zwischen synchronem und asynchronem Modus (Standard:sync
) -
kasan.stacktrace=[on|off]
– ob Stack-Traces erfasst werden sollen (Standard:on
)- Für die Stack-Trace-Erfassung ist außerdem
stack_depot_disable=off
erforderlich.
- Für die Stack-Trace-Erfassung ist außerdem
-
kasan.fault=[report|panic]
– ob nur der Bericht gedruckt oder auch der Kernel in Panik versetzt werden soll (Standard:report
). Unabhängig von dieser Option wird die Tag-Überprüfung nach dem ersten gemeldeten Fehler deaktiviert.
Empfohlene Verwendung
Wir empfehlen dringend, beim Hochfahren, Entwickeln und Testen den SYNC-Modus zu verwenden. Diese Option sollte global für alle Prozesse aktiviert werden, die die Umgebungsvariable oder mit dem Build-System verwenden. In diesem Modus werden Fehler frühzeitig im Entwicklungsprozess erkannt, die Codebasis wird schneller stabilisiert und die Kosten für die Erkennung von Fehlern später in der Produktion werden vermieden.
Wir empfehlen dringend, den ASYNC-Modus in der Produktion zu verwenden. Dies bietet ein Tool mit geringem Overhead zum Erkennen von Speichersicherheitsfehlern in einem Prozess sowie eine weitere Tiefenverteidigung. Sobald ein Fehler erkannt wird, kann der Entwickler die Laufzeit-APIs nutzen, um in den SYNC-Modus zu wechseln und einen genauen Stack-Trace von einer Stichprobe von Benutzern zu erhalten.
Wir empfehlen dringend, die CPU-spezifische bevorzugte MTE-Ebene für den SoC zu konfigurieren. Der Asymm-Modus hat normalerweise die gleichen Leistungsmerkmale wie ASYNC und ist diesem fast immer vorzuziehen. Kleine In-Order-Kerne zeigen in allen drei Modi oft eine ähnliche Leistung und können so konfiguriert werden, dass sie SYNC bevorzugen.
Entwickler sollten das Vorhandensein von Abstürzen überprüfen, indem sie /data/tombstones
oder logcat
überprüfen oder die DropboxManager
Pipeline des Anbieters auf Endbenutzerfehler überwachen. Weitere Informationen zum Debuggen von nativem Android-Code finden Sie hier .
MTE-fähige Plattformkomponenten
In Android 12 verwenden eine Reihe sicherheitskritischer Systemkomponenten MTE ASYNC, um Endbenutzerabstürze zu erkennen und als zusätzliche Ebene der Tiefenverteidigung zu fungieren. Diese Komponenten sind:
- Netzwerk-Daemons und Dienstprogramme (mit Ausnahme von
netd
) - Bluetooth, SecureElement, NFC-HALs und Systemanwendungen
-
statsd
Daemon -
system_server
-
zygote64
(um Anwendungen zu ermöglichen, sich für die Verwendung von MTE zu entscheiden)
Diese Ziele wurden anhand der folgenden Kriterien ausgewählt:
- Ein privilegierter Prozess (definiert als ein Prozess, der Zugriff auf etwas hat, was die SELinux-Domäne unprivileged_app nicht hat)
- Verarbeitet nicht vertrauenswürdige Eingaben ( Zweierregel )
- Akzeptable Leistungsverlangsamung (die Verlangsamung führt nicht zu einer für den Benutzer sichtbaren Latenz)
Wir ermutigen Anbieter, MTE in der Produktion für mehr Komponenten zu aktivieren und dabei die oben genannten Kriterien zu befolgen. Während der Entwicklung empfehlen wir, diese Komponenten im SYNC-Modus zu testen, um leicht zu behebende Fehler zu erkennen und die Auswirkungen von ASYNC auf ihre Leistung zu bewerten.
In Zukunft plant Android, die Liste der Systemkomponenten, auf denen MTE aktiviert ist, zu erweitern, basierend auf den Leistungsmerkmalen kommender Hardwaredesigns.