Warum MTE?
Arbeitsspeichersicherheitsfehler, also Fehler bei der Verarbeitung des Arbeitsspeichers in der nativen Programmierung Sprachen, sind häufige Codeprobleme. Sie führen zu Sicherheitslücken Stabilitätsprobleme.
Armv9 hat die Arm Memory Tagging Extension (MTE) eingeführt, eine Hardware, Erweiterung, die es Ihnen ermöglicht, Use-After-Free- und Puffer-Overflow-Fehler in Ihren nativen Code.
Support prüfen
Ab Android 13 unterstützen ausgewählte Geräte MTE. Um zu prüfen, ob auf Ihrem Gerät MTE aktiviert ist, führen Sie den folgenden Befehl aus: Befehl:
adb shell grep mte /proc/cpuinfo
Wenn das Ergebnis Features : [...] mte
lautet, wird auf Ihrem Gerät MTE ausgeführt
aktiviert.
Auf einigen Geräten wird MTE nicht standardmäßig aktiviert, Entwicklern wird jedoch der Neustart mit MTE aktiviert. Dies ist eine experimentelle Konfiguration, die für da sie die Leistung oder Stabilität des Geräts beeinträchtigen, nützlich für die App-Entwicklung. Um auf diesen Modus zuzugreifen, navigiere zu Entwickleroptionen > Memory Tagging Extension in der App „Einstellungen“ Wenn dieses nicht vorhanden ist, unterstützt Ihr Gerät das Aktivieren von MTE auf diese Weise nicht.
MTE-Betriebsmodi
MTE unterstützt zwei Modi: SYNC und ASYNC. Bessere Diagnose dank SYNC-Modus Informationen und ist daher besser für Entwicklungszwecke geeignet, während der ASYNC-Modus da es eine hohe Leistung aufweist, sodass es für veröffentlichte Apps aktiviert werden kann.
Synchroner Modus (SYNC)
Dieser Modus ist für die Fehlerbehebung optimiert und kann als präzises Tool zur Fehlererkennung verwendet werden, wenn akzeptabel. Wenn MTE SYNC aktiviert ist, dient auch die Sicherheitsminderung.
Bei einer Tag-Nichtübereinstimmung beendet der Prozessor den Prozess bei der problematischen Last oder Anweisungen zum Speichern mit SIGSEGV (mit si_code SEGV_MTESERR) und vollständigen Informationen zum Arbeitsspeicherzugriff und zur fehlerhaften Adresse.
Dieser Modus ist beim Testen als eine schnellere Alternative zu HWASan nützlich, die Sie müssen Ihren Code nicht neu kompilieren und auch nicht in der Produktion, wenn die App eine anfällige Angriffsfläche. Wenn der ASYNC-Modus (wie unten beschrieben) ein Fehler enthalten, können Sie einen genauen Fehlerbericht abrufen, indem Sie die Laufzeit-APIs verwenden, um den SYNC-Modus an.
Außerdem zeichnet der Android-Allocator im SYNC-Modus die Stacktraces jeder Zuweisung und Deallocation Fehlerberichte, die eine Erklärung eines Speicherfehlers enthalten, wie z. B. Use-After-Free oder Pufferüberlauf und die Stacktraces des entsprechenden Arbeitsspeichers Ereignissen (weitere Informationen finden Sie unter Informationen zu MTE-Berichten). Ein solches bieten mehr Kontextinformationen und erleichtern das Nachverfolgen von Fehlern als im ASYNC-Modus.
Asynchroner Modus (ASYNC)
Dieser Modus ist eher auf Leistung als auf Genauigkeit von Fehlerberichten ausgelegt und kann zur Erkennung von sicherheitsrelevanten Fehlern im Arbeitsspeicher. Bei nicht übereinstimmenden Tags Der Prozessor führt die Ausführung bis zum nächsten Kernel-Eintrag (z. B. syscall) oder Timer-Unterbrechung), wo er den Prozess mit SIGSEGV (Code SEGV_MTEAERR), ohne die fehlerhafte Adresse oder den fehlerhaften Arbeitsspeicherzugriff aufzuzeichnen.
Dieser Modus ist nützlich, um Sicherheitslücken im Arbeitsspeicher auf gut getesteten Codebasen zu arbeiten, bei denen die Dichte an Sicherheitsrisiken die als niedrig bekannt sind. Dies wird durch die Verwendung des SYNC-Modus beim Testen erreicht.
MTE aktivieren
Für ein einzelnes Gerät
Für Tests können Änderungen an der App-Kompatibilität verwendet werden, um die Standardeinstellung festzulegen
Wert des Attributs memtagMode
für eine Anwendung, die keine
beliebiger Wert im Manifest (oder "default"
angibt).
Diese finden Sie unter System > Erweitert > Entwickleroptionen > App-Kampagnen
Kompatibilitätsänderungen im globalen Einstellungsmenü NATIVE_MEMTAG_ASYNC
wird festgelegt
oder NATIVE_MEMTAG_SYNC
aktiviert MTE für eine bestimmte Anwendung.
Alternativ kann dies mit dem Befehl am
so festgelegt werden:
- Für den SYNC-Modus:
$ adb shell am compat enable NATIVE_MEMTAG_SYNC my.app.name
- Für den ASYNC-Modus:
$ adb shell am compat enable NATIVE_MEMTAG_ASYNC my.app.name
In Gradle
Sie können MTE für alle Debug-Builds Ihres Gradle-Projekts aktivieren, indem Sie
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application android:memtagMode="sync" tools:replace="android:memtagMode"/>
</manifest>
in app/src/debug/AndroidManifest.xml
. Dadurch wird die
memtagMode
mit Synchronisierung für Debug-Builds.
Alternativ können Sie MTE für alle Builds eines benutzerdefinierten buildType aktivieren. Aufgabe
Erstellen Sie also Ihren eigenen "buildType" und fügen Sie
XML in app/src/<name of buildType>/AndroidManifest.xml
umwandeln.
Für ein APK auf einem beliebigen kompatiblen Gerät
MTE ist standardmäßig deaktiviert. Apps, die MTE nutzen möchten, können
Legen Sie dazu android:memtagMode
unter <application>
oder <process>
fest.
-Tag im AndroidManifest.xml
.
android:memtagMode=(off|default|sync|async)
Wenn das Attribut für das <application>
-Tag festgelegt wird, wirkt es sich auf alle verwendeten Prozesse aus
festgelegt und kann für einzelne Prozesse überschrieben werden. Legen Sie dazu
das <process>
-Tag.
Mit Instrumentierung erstellen
Wenn Sie MTE wie zuvor erläutert aktivieren, können Fehler im Arbeitsspeicher nativer Heap. Um Speicherbeschädigungen im Stack zu erkennen, MTE für die App ist, muss der Code mit Instrumentierung neu erstellt werden. Die damit die App nur auf MTE-fähigen Geräten läuft.
So erstellen Sie den nativen Code Ihrer Anwendung (JNI) mit MTE:
NK-Build
In der Datei Application.mk
:
APP_CFLAGS := -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag
APP_LDFLAGS := -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag
CMake
Führen Sie für jedes Ziel in der Datei CMakeLists.txt folgende Schritte aus:
target_compile_options(${TARGET} PUBLIC -fsanitize=memtag -fno-omit-frame-pointer -march=armv8-a+memtag)
target_link_options(${TARGET} PUBLIC -fsanitize=memtag -fsanitize-memtag-mode=sync -march=armv8-a+memtag)
App ausführen
Verwenden und testen Sie Ihre Anwendung nach der Aktivierung von MTE wie gewohnt. Bei Problemen mit der Speichersicherheit
erkannt wurde, stürzt Ihre App mit einem Tombstone ab, der diesem ähnelt (Hinweis:
SIGSEGV
mit SEGV_MTESERR
für SYNC oder SEGV_MTEAERR
für ASYNC):
pid: 13935, tid: 13935, name: sanitizer-statu >>> sanitizer-status <<<
uid: 0
tagged_addr_ctrl: 000000000007fff3
signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x800007ae92853a0
Cause: [MTE]: Use After Free, 0 bytes into a 32-byte allocation at 0x7ae92853a0
x0 0000007cd94227cc x1 0000007cd94227cc x2 ffffffffffffffd0 x3 0000007fe81919c0
x4 0000007fe8191a10 x5 0000000000000004 x6 0000005400000051 x7 0000008700000021
x8 0800007ae92853a0 x9 0000000000000000 x10 0000007ae9285000 x11 0000000000000030
x12 000000000000000d x13 0000007cd941c858 x14 0000000000000054 x15 0000000000000000
x16 0000007cd940c0c8 x17 0000007cd93a1030 x18 0000007cdcac6000 x19 0000007fe8191c78
x20 0000005800eee5c4 x21 0000007fe8191c90 x22 0000000000000002 x23 0000000000000000
x24 0000000000000000 x25 0000000000000000 x26 0000000000000000 x27 0000000000000000
x28 0000000000000000 x29 0000007fe8191b70
lr 0000005800eee0bc sp 0000007fe8191b60 pc 0000005800eee0c0 pst 0000000060001000
backtrace:
#00 pc 00000000000010c0 /system/bin/sanitizer-status (test_crash_malloc_uaf()+40) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
#01 pc 00000000000014a4 /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
#02 pc 00000000000019cc /system/bin/sanitizer-status (main+1032) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
#03 pc 00000000000487d8 /apex/com.android.runtime/lib64/bionic/libc.so (__libc_init+96) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
deallocated by thread 13935:
#00 pc 000000000004643c /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::quarantineOrDeallocateChunk(scudo::Options, void*, scudo::Chunk::UnpackedHeader*, unsigned long)+688) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
#01 pc 00000000000421e4 /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::deallocate(void*, scudo::Chunk::Origin, unsigned long, unsigned long)+212) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
#02 pc 00000000000010b8 /system/bin/sanitizer-status (test_crash_malloc_uaf()+32) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
#03 pc 00000000000014a4 /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
allocated by thread 13935:
#00 pc 0000000000042020 /apex/com.android.runtime/lib64/bionic/libc.so (scudo::Allocator<scudo::AndroidConfig, &(scudo_malloc_postinit)>::allocate(unsigned long, scudo::Chunk::Origin, unsigned long, bool)+1300) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
#01 pc 0000000000042394 /apex/com.android.runtime/lib64/bionic/libc.so (scudo_malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
#02 pc 000000000003cc9c /apex/com.android.runtime/lib64/bionic/libc.so (malloc+36) (BuildId: 6ab39e35a2fae7efbe9a04e9bbb14331)
#03 pc 00000000000010ac /system/bin/sanitizer-status (test_crash_malloc_uaf()+20) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
#04 pc 00000000000014a4 /system/bin/sanitizer-status (test(void (*)())+132) (BuildId: 953fc93301472d0b72709b2b9a9f6f30)
Learn more about MTE reports: https://source.android.com/docs/security/test/memory-safety/mte-report
Weitere Informationen finden Sie in der AOSP-Dokumentation unter Informationen zu MTE-Berichten. Ich Sie können auch Fehler in Ihrer App mit Android Studio beheben. Die Fehlerbehebung endet, wenn Sie Zeile, die den ungültigen Speicherzugriff verursacht.
Fortgeschrittene Nutzer: MTE in Ihrem eigenen Allocator verwenden
Um MTE für Arbeitsspeicher zu verwenden, der nicht über die normalen Systemzuordnungen zugewiesen ist, müssen Sie Ihren Zuweisungsfilter ändern, um Arbeitsspeicher und Zeiger zu taggen.
Die Seiten für Ihren Allocator müssen mithilfe von PROT_MTE
in der
prot
-Flag von mmap
(oder mprotect
).
Alle getaggten Zuweisungen müssen auf 16 Byte ausgerichtet sein, da Tags nur zugewiesen werden können. für 16-Byte-Blöcke (auch als Granulen bezeichnet).
Bevor Sie einen Zeiger zurückgeben, müssen Sie die Anweisung IRG
verwenden, um
ein zufälliges Tag
generieren und im Pointer speichern.
So taggen Sie den zugrunde liegenden Arbeitsspeicher:
STG
: eine einzelne 16-Byte-Granule taggenST2G
: Zwei 16-Byte-Granulen taggenDC GVA
: Tag-Cache-Zeile mit demselben Tag
Alternativ wird mit den folgenden Anweisungen auch der Speicher mit Nullen initialisiert:
STZG
: mit Tags versehen und mit Null eine einzelne 16-Byte-Granule initialisierenSTZ2G
: Zwei 16-Byte-Granulen taggen und mit Null initialisierenDC GZVA
: Tag und Cache-Zeile mit Nullinitialisierung mit demselben Tag
Beachten Sie, dass diese Anleitung auf älteren CPUs nicht unterstützt wird. Sie führen sie bedingt aus, wenn MTE aktiviert ist. Sie können prüfen, ob MTE die für Ihren Prozess aktiviert sind:
#include <sys/prctl.h>
bool runningWithMte() {
int mode = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
return mode != -1 && mode & PR_MTE_TCF_MASK;
}
Die Scudo-Implementierung kann als Referenz hilfreich sein.
Weitere Informationen
Weitere Informationen finden Sie im MTE-Nutzerhandbuch für das Android-Betriebssystem von Arm.