Schwerpunkte

Sicherheitslücke in Mikroprozessoren

Der CPU-Super-GAU: Spectre, Meltdown und die Gegenmaßnahmen

07. Januar 2018, 09:46 Uhr   |  Frank Riemenschneider


Fortsetzung des Artikels von Teil 1 .

Spectre Variante 1: Speicherleck durch spekulative Befehlsausführung

Die „Idee“ der Forscher für die erste Spectre-Variante bestand einfach gesagt darin, auszunutzen, dass spekulativ ausgeführte und später verworfene Programmteile Daten aus Bereichen in den Cache laden können, auf die sie normalerweise keinen Zugriff hätten und die Rückschlüsse auf geheime Informationen liefern können.

An Hand des folgenden Codebeispiels, das von ARMs Chefarchitekt Richard Grisenthwaite stammt, soll das Vorgehen des Hackers und eine wirkungsvolle Gegenmaßnahme diskutiert werden.

1 struct array {
2 unsigned long length;
3 unsigned char data[];
4 };
5 struct array *arr = ...;
6 unsigned long untrusted_offset_from_user = ...;
7 if (untrusted_offset_from_user < arr->length) {
8 unsigned char value;
9 value =arr->data[untrusted_offset_from_user];
10 …
11 }

In diesem Code wird ein Datenbyte „value“ aus einem Array gelesen, wobei sich die Adresse aus der Basis des Arrays und einem nichtvertrauenswürdigen Offset, der durch den User übergeben wird, ergibt. Falls der Offset die vorgegebenen Grenzen des Arrays überschreitet, wird der Wert nicht ausgelesen und übergeben. Auch bei einer spekulativen Ausführung ist dies kein Problem, weil im Fall einer Fehlspekulation der gelesene Wert durch die Hardware verworfen wird.

Das Problem ist nunmehr, dass Hochleistungs-CPUs mit spekulativen Daten weiterspekulieren können, was der folgende Code zeigt:

1 struct array {
2 unsigned long length;
3 unsigned char data[];
4 };
5 struct array *arr1 = ...; /* kleines array */
6 struct array *arr2 = ...; /*array der Größe 0x400 */
7 unsigned long untrusted_offset_from_user = ...;
8 if (untrusted_offset_from_user < arr1->length) {
9 unsigned char value;
10 value =arr1->data[untrusted_offset_from_user];
11 unsigned long index2 =((value&1)*0x100)+0x200;
12 if (index2 < arr2->length) {
13 unsigned char value2 = arr2->data[index2];
14 }
15 }

In diesem Fall wird der gelesene Byte-Wert „value“ verwendet, um einen Offset für einen zweiten Zugriff auf ein weiteres Array „arr2“ vorzunehmen und hier einen Wert „value2“ auszulesen. Dieser spekulative Lesezugriff kann eine Veränderung des Cache-Status auslösen und zwar adressspezifisch, wobei die Adresse van „value“ abhängt. Wenn der Hacker von für alle Zahlen von n=0 bis 255 einen Lesezugriff startet, wird dieser für n=value  durch den Cache schnell und sonst langsam sein. Es läuft also wieder auf eine Cache-Timing-Analyse hinaus, ein bekanntes Verfahren, hier mit einem neuen Ansatz. Am Ende kennt der Hacker den Wert von „value“, den er bei einem Offset außerhalb der legalen Array-Grenzen gar nicht hätte auslesen dürfen.

Gegenmaßnahmen für Spectre Variante 1

Um das soeben geschilderte Leck zu schließen, reicht bei vielen ARM-Implementierungen eine CSEL-Anweisung (64-bit-Modus AArch64, wie diese eingebaut wird, später) bzw. eine bedingte MOVE-Anweisung (32-bit-Modus AArch32) aus. Wo diese nicht ausreicht, kann eine neue CSDB genannte Barriere (Conditional Speculation Barrier) eingefügt werden. Diese zusammen mit CSEL verhindert diese Spectre-Variante auf allen betroffenen ARM-CPUs.

Der oben beschriebene C-Code würde in Assembler-Instruktionen im 64-bit-Mode AArch64 wie folgt aussehen:

LDR X1, [X2] ; X2 ist ein Zeiger auf arr1->length
CMP X0, X1 ; X0 enthält untrusted_offset_from_user
BGE out_of_range
LDRB W4, [X5,X0] ; X5 enthält arr1->data base
AND X4, X4, #1
LSL X4, X4, #8
ADD X4, X4, #0x200
CMP X4, X6 ; X6 enthält arr2->length
BGE out_of_range
LDRB X7, [X8, X4] ; X8 enthält arr2->data base
out_of_range

Ändert man den Code wie folgt, hat sich Spectre Variante 1 erledigt:

LDR X1, [X2] ; X2 ist Zeiger auf arr1->length
CMP X0, X1 ; X0 enthält untrusted_offset_from_user
BGE out_of_range
LDRB W4, [X5,X0] ; X5 beinhaltet arr1->data base
CSEL X4, XZR, X4, GE  ; X4 auf Null setzen, wenn Offset außerhalb zulässiger Bereich
CSDB ; Neue Barriere
AND X4, X4, #1
LSL X4, X4, #8
ADD X4, X4, #0x200
CMP X4, X6 ; X6 enthält arr2->length
BGE out_of_range
LDRB X7, [X8, X4] ; X8 beinhaltet arr2->data base
out_of_range

Die CSEL-Anweisung fragt exakt die Bedingung „größergleich“ der bedingten Verzweigung BGE ab. Ist diese erfüllt, wird der illegale Offset in Register X4 auf Null zurückgesetzt, bevor spekulativ weiter ausgeführt und der sicherheitskritische Wert geladen wird. In Implementierungen, wo dies nicht funktioniert, verhindert die Barriere CSDB die weitere Programmausführung, bevor die Instruktionen davor abgeschlossen sind. Damit kommt es dann gar nicht mehr zu einer weiteren spekulativen Ausführung, wenn sich der Offset außerhalb der zulässigen Grenzen bewegt. Auf CPU-Implementierungen, wo CSEL funktioniert, wird CDSB zu einem NOP. In ähnlicher Weise lässt sich auch AArch32-Code modifizieren. Hier würde statt CSEL eine Instruktion MOVEGE R4, #0 zum Einsatz kommen, um den Offset bei einem illegalen Wert außerhalb der zulässigen Grenzen auf Null zurückzusetzen.

Die Kombination beider Instruktionen verhindert Spectre Variante 1 in allen Implementierungen der betroffenen Cortex-Cores.

Seite 2 von 9

1. Der CPU-Super-GAU: Spectre, Meltdown und die Gegenmaßnahmen
2. Spectre Variante 1: Speicherleck durch spekulative Befehlsausführung
3. Spectre Variante 2: Speicherleck durch Gadgets
4. Meltdown: Speicherleck trotz Speicherschutzverletzung
5. Fazit
6. Die betroffenen Intel- und ARM-CPUs
7. Das ist das Statement von Texas Instruments für Sitara und andere SoCs
8. Das ist das Statement von NXP für i.MX
9. Das ist das Statement von Renesas

Auf Facebook teilen Auf Twitter teilen Auf Linkedin teilen Via Mail teilen

Verwandte Artikel

INTEL GmbH, ARM Germany GmbH