MTE-Berichte auswerten

SIGSEGV-Abstürze mit dem Code 9 (SEGV_MTESERR) oder Code 8 (SEGV_MTEAERR) sind Speicher-Tagging-Fehler. Memory Tagging Extension (MTE) ist eine Die ARMv9-Funktion wird ab Android 12 unterstützt. MTE ist eine Hardware-Implementierung zu speichern. Sie bietet einen differenzierten Speicherschutz zur Erkennung und Beseitigung von Memory-Sicherheitsfehler.

In C/C++ kann ein von einem Aufruf an "maloc()" oder dem "operator new()" oder ähnlicher Funktionen zurückgegebener Zeiger nur für den Zugriff auf Speicher innerhalb der Grenzen dieser Zuweisung verwendet werden, Zuweisung aktiv ist (nicht freigegeben oder gelöscht). MTE wird in Android verwendet, um Verstöße gegen Diese Regel wird in den Absturzberichten als „Pufferüberlauf“/„Pufferunterlauf“ bezeichnet. und „Nach dem kostenlosen Angebot nutzen“ Probleme.

MTE verfügt über zwei Modi: synchron (oder "sync") und asynchron (oder "asynchron"). Ersterer läuft mehr ist langsam, bietet aber eine genauere Diagnose. Letztere läuft schneller, kann aber nur ungefähre Angaben machen. Da sich die Diagnosen etwas unterscheiden, werden wir beide separat behandeln.

Synchroner Modus MTE

Im synchronen Modus ("Sync") von MTE stürzt SIGSEGV mit dem Code 9 (SEGV_MTESERR) ab.

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)

Alle MTE-Absturzberichte enthalten die üblichen Registrierungs-Dump- und Backtraces für den Punkt, an dem der ein Problem festgestellt wurde. Die Ursache: Die Zeile für einen von MTE erkannten Fehler enthält "[MTE]". wie in im Beispiel oben, einschließlich weiterer Details. In diesem Fall war die Art des erkannten Fehlers a „Use after free“ (Nach dem kostenlosen Verwenden verwenden) und „0 bytes in a 32-byte Zuweisung at 0x7ae92853a0“ (0 Byte in einer 32-Byte-Zuweisung bei 0x7ae92853a0) teilt uns mit, Größe und Adresse der Zuweisung und den Versatz in die Zuweisung, auf die wir zugreifen wollten.

MTE-Absturzberichte enthalten auch zusätzliche Backtraces, nicht nur den ab dem Zeitpunkt der Erkennung.

„Nach dem kostenlosen Angebot nutzen“ Fehler hinzufügen "Zuweisung aufgehoben von" und „zugewiesen von“ zum Crash-Dump, zeigt die Stacktraces zum Zeitpunkt der Freigabe dieses Arbeitsspeichers (vor der Verwendung) und der Zeit, die zuvor zugewiesen wurde. Sie sehen auch, in welchem Thread Zuweisen/Abgeben. Alle drei Threads erkennen, Zuweisen von Threads und Freigeben von Threads sind in diesem einfachen Beispiel gleich, aber in komplexeren Fällen die nicht unbedingt wahr sind. Das Wissen, dass sie unterschiedlich sind, kann ein wichtiger Hinweis sein, um eine auf Nebenläufigkeit zurückzuführen.

„Pufferüberlauf“ und „Buffer Underflow“ stellen nur eine zusätzliche "Zugewiesen von" da sie per Definition noch nicht freigegeben wurden (oder als "Nach der kostenlosen Nutzung verwenden"):

Cause: [MTE]: Buffer Overflow, 0 bytes right of a 32-byte allocation at 0x7ae92853a0
[...]
backtrace:
[...]
allocated by thread 13949:

Das Wort „rechts“ Hier sehen Sie, wie viele Bytes nach dem Ende der Zuordnung war der falsche Zugriff würde ein Unterlauf „left“ heißen und eine Reihe von Byte vor dem Beginn der Zuweisung.

Mehrere mögliche Ursachen

Manchmal enthalten SEGV_MTESERR-Berichte die folgende Zeile:

Note: multiple potential causes for this crash were detected, listing them in decreasing order of likelihood.

Dies ist der Fall, wenn es mehrere geeignete Kandidaten für die Fehlerquelle gibt und wir keine das die eigentliche Ursache ist. Wir drucken bis zu drei dieser Kandidaten in der ungefähren Reihenfolge ihrer Wahrscheinlichkeit aus. und die Analyse den Nutzenden überlassen.

signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x400007b43063db5
backtrace:
    [stack...]

Note: multiple potential causes for this crash were detected, listing them in decreasing order of probability.

Cause: [MTE]: Use After Free, 5 bytes into a 10-byte allocation at 0x7b43063db0
deallocated by thread 6663:
    [stack...]
allocated by thread 6663:
    [stack...]

Cause: [MTE]: Use After Free, 5 bytes into a 6-byte allocation at 0x7b43063db0
deallocated by thread 6663:
    [stack...]

allocated by thread 6663:
    [stack...]

Im obigen Beispiel haben wir zwei kürzlich erfolgte Zuweisungen unter derselben Speicheradresse erkannt, das vorgesehene Ziel des ungültigen Arbeitsspeicherzugriffs waren. Das kann passieren, wenn Zuweisungen kostenlosen Speicher – z. B. für die Sequenz "new", "free", "new", "free", "new", "free", Zugriff haben. Die neuere Zuweisung wird zuerst gedruckt.

Detaillierte Heuristiken zur Ursachenbestimmung

Die „Ursache“ eines Absturzes sollte die Arbeitsspeicherzuweisung anzeigen, von der der Zugriffszeiger ursprünglich abgeleitet wurde. Leider hat MTE-Hardware keine Möglichkeit, von einem Zeiger mit einem nicht übereinstimmenden Tag in eine Zuordnung zu übertragen. Android analysiert die folgenden Daten, um einen SEGV_MTESERR-Absturz zu erklären:

  • Die Fehleradresse (einschließlich Pointer-Tag).
  • Eine Liste der letzten Heap-Zuweisungen mit Stacktraces und Speicher-Tags.
  • Aktuelle (Live-)Zuweisungen in der Nähe und ihre Speicher-Tags.

Jeder kürzlich freigegebene Arbeitsspeicher an der Fehleradresse, bei der das Speicher-Tag mit dem Fehleradressen-Tag übereinstimmt, ist ein potenzielles „Nach dem Freigeben verwenden“ Ursache.

Jeder Live-Speicher in der Nähe, bei dem das Speicher-Tag mit dem Fehleradressen-Tag übereinstimmt, ist ein potenzieller „Pufferüberlauf“. (oder „Unterlauf Puffer“).

Zuweisungen, die näher am Fehler liegen – entweder zeitlich oder räumlich – gelten als wahrscheinlicher als weit entfernte Zuweisungen.

Da freigegebener Speicher häufig wiederverwendet wird und die Anzahl der verschiedenen Tag-Werte gering ist (weniger als 16), ist es nicht ungewöhnlich, mehrere mögliche Kandidaten zu finden, und es gibt keine Möglichkeit, automatisch die tatsächliche Ursache zu ermitteln. Dies ist der Grund, warum MTE-Berichte manchmal mehrere mögliche Ursachen auflisten.

Es wird empfohlen, dass der App-Entwickler mögliche Ursachen untersucht, beginnend mit der wahrscheinlichsten. Es ist oft einfach, auf der Grundlage des Stacktrace irrelevante Ursachen herauszufiltern.

Asynchroner Modus (MTE)

Im asynchronen Modus (async) von MTE stürzt SIGSEGV mit Code 8 (SEGV_MTEAERR) ab.

SEGV_MTEAERR-Fehler treten nicht sofort auf, wenn ein Programm einen ungültigen Speicherzugriff ausführt. Das Problem wird kurz nach dem Ereignis erkannt und das Programm wird stattdessen beendet. Dieser Punkt ist normalerweise der nächste Systemaufruf, kann aber auch eine Timer-Unterbrechung sein – kurz gesagt ein Wechsel von einem Userspace zum Kernel.

SEGV_MTEAERR-Fehler speichern die Speicheradresse nicht (sie wird immer als "-------" angezeigt). Die Rückverfolgung entspricht dem Zeitpunkt, an dem die Bedingung erkannt wurde (d.h. beim nächsten Systemaufruf oder einem anderen Kontextwechsel), und nicht dem Zeitpunkt, zu dem der ungültige Zugriff ausgeführt wurde.

Das bedeutet, dass die „Haupt“- Backtrace bei einem asynchronen MTE-Absturz ist normalerweise nicht relevant. Fehler im asynchronen Modus sind daher wesentlich schwieriger zu beheben als Fehler im Synchronisierungsmodus. Sie sind am besten so zu verstehen, dass sie das Vorhandensein eines Gedächtnisfehlers im nahe gelegenen Code des jeweiligen Threads zeigen. Protokolle am Ende der Tombstone-Datei können Hinweise auf das tatsächliche Geschehen geben. Andernfalls sollten Sie den Fehler im Synchronisierungsmodus reproduzieren und die bessere Diagnosefunktion des Synchronisierungsmodus verwenden.

Erweiterte Themen

Intern wird beim Arbeitsspeicher-Tagging jede Heap-Zuweisung ein zufälliger 4-Bit-Tag-Wert (0–15) zugewiesen. Dieser Wert wird in einer speziellen Metadatenregion gespeichert, die dem zugewiesenen Heap-Speicher entspricht. Derselbe Wert wird dem höchstwertigen Byte des Heap-Pointers zugewiesen, der von Funktionen wie "maloc()" oder "operator new()" zurückgegeben wird.

Wenn die Tag-Überprüfung dabei aktiviert ist, vergleicht die CPU bei jedem Speicherzugriff automatisch das oberste Byte des Zeigers mit dem Speicher-Tag. Wenn die Tags nicht übereinstimmen, signalisiert die CPU einen Fehler, der zu einem Absturz führt.

Aufgrund der begrenzten Anzahl möglicher Tag-Werte ist dieser Ansatz probabilistisch. Jeder Speicherort, auf den mit einem bestimmten Zeiger nicht zugegriffen werden soll, z. B. außerhalb des festgelegten Bereichs oder nach der Freigabe ("dangling Pointer"), hat wahrscheinlich einen anderen Tag-Wert und verursacht einen Absturz. Die Wahrscheinlichkeit, dass ein Fehler nicht nur einmal erkannt wird, liegt bei etwa 7 %. Da die Tag-Werte zufällig zugewiesen werden, besteht eine unabhängige Wahrscheinlichkeit von ca. 93 %, dass der Fehler beim nächsten Auftreten des Fehlers erkannt wird.

Die Tag-Werte können im Fehleradressenfeld sowie im Register-Dump angezeigt werden, wie unten hervorgehoben. In diesem Bereich können Sie prüfen, ob die Tags vernünftig gesetzt sind. Außerdem können Sie andere Arbeitsspeicherzuweisungen in der Nähe mit demselben Tag-Wert sehen, da sie potenzielle weitere Ursachen für den Fehler sein können, die über die im Bericht aufgeführten hinausgehen. Wir erwarten, dass dies vor allem für die Personen nützlich ist, die an der Implementierung von MTE selbst oder anderen untergeordneten Systemkomponenten arbeiten, und nicht für Entwickler.

signal 11 (SIGSEGV), code 9 (SEGV_MTESERR), fault addr 0x0800007ae92853a0
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

Spezielle „Speicher-Tags“ Abschnitt auch im Absturzbericht angezeigt, der Speicher-Tags um die Fehleradresse herum enthält. Im folgenden Beispiel hat das Zeiger-Tag „4“ stimmt nicht mit dem Speicher-Tag "a" überein.

Memory tags around the fault address (0x0400007b43063db5), one tag per 16 bytes:
  0x7b43063500: 0  f  0  2  0  f  0  a  0  7  0  8  0  7  0  e
  0x7b43063600: 0  9  0  8  0  5  0  e  0  f  0  c  0  f  0  4
  0x7b43063700: 0  b  0  c  0  b  0  2  0  1  0  4  0  7  0  8
  0x7b43063800: 0  b  0  c  0  3  0  a  0  3  0  6  0  b  0  a
  0x7b43063900: 0  3  0  4  0  f  0  c  0  3  0  e  0  0  0  c
  0x7b43063a00: 0  3  0  2  0  1  0  8  0  9  0  4  0  3  0  4
  0x7b43063b00: 0  5  0  2  0  5  0  a  0  d  0  6  0  d  0  2
  0x7b43063c00: 0  3  0  e  0  f  0  a  0  0  0  0  0  0  0  4
=>0x7b43063d00: 0  0  0  a  0  0  0  e  0  d  0 [a] 0  f  0  e
  0x7b43063e00: 0  7  0  c  0  9  0  a  0  d  0  2  0  0  0  c
  0x7b43063f00: 0  0  0  6  0  b  0  8  0  3  0  0  0  5  0  e
  0x7b43064000: 0  d  0  2  0  7  0  a  0  7  0  a  0  d  0  8
  0x7b43064100: 0  b  0  2  0  b  0  4  0  1  0  6  0  d  0  4
  0x7b43064200: 0  1  0  6  0  f  0  2  0  f  0  6  0  5  0  c
  0x7b43064300: 0  1  0  4  0  d  0  6  0  f  0  e  0  1  0  8
  0x7b43064400: 0  f  0  4  0  3  0  2  0  1  0  2  0  5  0  6

Abschnitte eines Tombstones, die den Speicherinhalt um alle Registerwerte herum zeigen, zeigen auch deren Tag-Werte an.

memory near x10 ([anon:scudo:primary]):
0000007b4304a000 7e82000000008101 000003e9ce8b53a0  .......~.S......
0700007b4304a010 0000200000006001 0000000000000000  .`... ..........
0000007b4304a020 7c03000000010101 000003e97c61071e  .......|..a|....
0200007b4304a030 0c00007b4304a270 0000007ddc4fedf8  p..C{.....O.}...
0000007b4304a040 84e6000000008101 000003e906f7a9da  ................
0300007b4304a050 ffffffff00000042 0000000000000000  B...............
0000007b4304a060 8667000000010101 000003e9ea858f9e  ......g.........
0400007b4304a070 0000000100000001 0000000200000002  ................
0000007b4304a080 f5f8000000010101 000003e98a13108b  ................
0300007b4304a090 0000007dd327c420 0600007b4304a2b0   .'.}......C{...
0000007b4304a0a0 88ca000000010101 000003e93e5e5ac5  .........Z^>....
0a00007b4304a0b0 0000007dcc4bc500 0300007b7304cb10  ..K.}......s{...
0000007b4304a0c0 0f9c000000010101 000003e9e1602280  ........."`.....
0900007b4304a0d0 0000007dd327c780 0700007b7304e2d0  ..'.}......s{...
0000007b4304a0e0 0d1d000000008101 000003e906083603  .........6......
0a00007b4304a0f0 0000007dd327c3b8 0000000000000000  ..'.}...........