ARM-Speicher-Tagging-Erweiterung (MTE)

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 taggen
  • ST2G: Zwei 16-Byte-Granulen taggen
  • DC 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 initialisieren
  • STZ2G: Zwei 16-Byte-Granulen taggen und mit Null initialisieren
  • DC 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.