Hacking Warum Raspberry Pi sicher vor Meltdown und Spectre ist

Margit Kuther

Anbieter zum Thema

Spectre und Meltdown attackieren moderne Intel-Prozessoren und – im Fall von Spectre – viele AMD-Prozessoren und ARM-Cores. Nicht aber Raspberry Pi. Raspberry-Pi-Initiator Eben Upton verrät, warum.

Raspberry-Pi-Platinen im Überblick: Sie sind immun gegen Side-Channel-Attacks wie sie Meltdown und Spectre nutzen
Raspberry-Pi-Platinen im Überblick: Sie sind immun gegen Side-Channel-Attacks wie sie Meltdown und Spectre nutzen
(Bild: Thomas Kuther)

Mit Spectre kann ein Angreifer Softwareüberprüfungen umgehen, um Daten von beliebigen Speicherorten im aktuellen Adressraum zu lesen.

Meltdown ermöglicht es einem Angreifer, Daten von beliebigen Speicherorten im Adressraum des Betriebssystemkerns zu lesen, auf die Benutzerprogramme normalerweise keinen Zugriff haben.

Bildergalerie
Bildergalerie mit 9 Bildern

Beide Sicherheitslücken nutzen Technologien moderner Prozessoren, die diesen einen Geschwindigkeitsvorteil verschaffen. Dazu zählt etwa vorausschauendes Laden von Speicherinhalten in den schnelleren Cache. Allerdings hat dies den Nachteil, dass Daten über einen so genannten Seitenkanalangriff (Side-Channel-Attack) unerlaubterweise ausgelesen werden können.

„Glücklicherweise ist der Raspberry Pi aufgrund der speziellen ARM-Kerne (siehe Bildergalerie), die wir verwenden, nicht anfällig für diese Sicherheitsanfälligkeiten“, so Eben Upton, einer der Initiatoren des Raspberry Pis.

Warum das so ist, veranschaulicht Eben Upton in folgender Python-Syntax und gibt gleichzeitig einen Einblick in die allgemeine Prozessortechnologie:

t = a + b

u = c + d

v = e + f

w = v + g

x = h + i

y = j + k

Während der Prozessor im Computer Python nicht direkt ausführt, sind die Anweisungen hier so einfach, dass sie in etwa einem einzelnen Maschinenbefehl entsprechen. Wir werden einige Details (insbesondere das Pipelining und das Umbenennen von Registern), die für Prozessor-Designer sehr wichtig sind, aber die nicht notwendig sind, um zu verstehen, wie Spectre und Meltdown funktionieren, unter den Teppich kehren.

Skalar- und Superskalarprozessor

Die einfachste Art eines modernen Prozessors führt einen Befehl pro Zyklus aus; Wir nennen dies einen Skalarprozessor. Unser Beispiel oben wird in sechs Zyklen auf einem Skalarprozessor ausgeführt. Beispiele für Skalarprozessoren sind der Intel 486 und der ARM1176-Kern, der in Raspberry Pi 1 und Raspberry Pi Zero verwendet wird.

Die offensichtliche Möglichkeit, einen Skalarprozessor (oder einen Prozessor) schneller laufen zu lassen, ist die Erhöhung der Taktfrequenz. Wir erreichen jedoch bald Grenzen dafür, wie schnell die Logikgatter innerhalb des Prozessors zum Laufen gebracht werden können; Prozessor-Entwickler begannen daher nach Möglichkeiten zu suchen, mehrere Dinge gleichzeitig zu tun.

Ein superskalarer Prozessor untersucht in der Reihenfolge den eingehenden Befehlsstrom und versucht, mehr als eines gleichzeitig in einer von mehreren Pipelines (kurz Pipes) auszuführen, abhängig von den Abhängigkeiten zwischen den Anweisungen. Abhängigkeiten sind wichtig: Sie könnten denken, dass ein Zwei-Wege-Superskalar-Prozessor die sechs Anweisungen in unserem Beispiel einfach so paaren (oder zweifach ausgeben) kann:

t, u = a + b, c + d

v, w = e + f, v + g

x, y = h + i, j + k

Aber das macht keinen Sinn: Wir müssen v berechnen, bevor wir w berechnen können, damit die dritte und vierte Anweisung nicht gleichzeitig ausgeführt werden können. Unser Zwei-Wege-Superskalar-Prozessor wird tatsächlich nichts finden, um mit der dritten Anweisung zu koppeln, also wird unser Beispiel in vier Zyklen ausgeführt:

t, u = a + b, c + d

v = e + f # zweite Pipe tut hier nichts

w, x = v + g, h + i

y = j + k

Beispiele für superskalare Prozessoren sind der Intel Pentium und die ARM Cortex-A7- und Cortex-A53-Kerne, die in Raspberry Pi 2 bzw. Raspberry Pi 3 verwendet werden. Raspberry Pi 3 hat nur eine 33% höhere Taktrate als Raspberry Pi 2, aber hat ungefähr die doppelte Leistung: Die zusätzliche Leistung ist teilweise eine Folge der Fähigkeit von Cortex-A53, eine breitere Palette von Anweisungen als Cortex-A7 Dual-Ausgabe.

Was ist ein Out-of-Order-Prozessor?

Wenn wir zurück zu unserem Beispiel gehen, können wir sehen, dass, obwohl wir eine Abhängigkeit zwischen v und w haben, wir später im Programm andere unabhängige Anweisungen haben, die wir möglicherweise benutzt haben, um die leere Pipe während des zweiten Zyklus zu füllen. Ein außer Betrieb befindlicher superskalarer Prozessor hat die Fähigkeit, die Reihenfolge eingehender Anweisungen zu mischen (wiederum abhängig von Abhängigkeiten), um seine Leitungen beschäftigt zu halten.

Ein Out-of-Order-Prozessor könnte die Definitionen von w und x in unserem Beispiel folgendermaßen ersetzen:

Jetzt Newsletter abonnieren

Verpassen Sie nicht unsere besten Inhalte

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

t = a + b

u = c + d

v = e + f

x = h + i

w = v + g

y = j + k

erlaubt es in drei Zyklen auszuführen:

t, u = a + b, c + d

v, x = e + f, h + i

w, y = v + g, j + k

Beispiele für Out-of-Order-Prozessoren sind der Intel Pentium 2 (und die meisten nachfolgenden Intel- und AMD x86-Prozessoren mit Ausnahme einiger Atom- und Quark-Geräte) und viele neue ARM-Cores, darunter Cortex-A9, -A15, -A17, und -A57.

Was ist eine Verzweigungsvorhersage?

Unser Beispiel oben ist ein geradliniger Code. Echte Programme sind natürlich nicht so: Sie enthalten auch sowohl Vorwärtszweige (die zum Implementieren bedingter Operationen wie if-Anweisungen verwendet werden) als auch Rückwärtszweige (die zum Implementieren von Schleifen verwendet werden). Eine Verzweigung kann unbedingt (immer genommen) oder bedingt (abhängig von einem berechneten Wert) sein; es kann direkt sein (explizite Angabe einer Zieladresse) oder indirekt (wobei seine Zieladresse von einem Register, einer Speicherstelle oder dem Prozessorstapel genommen wird).

Während des Abrufens von Anweisungen kann ein Prozessor auf eine bedingte Verzweigung treffen, die von einem Wert abhängt, der noch zu berechnen ist. Um einen Stillstand zu vermeiden, muss er vorhersehen, welcher Befehl als nächstes zu holen ist: der nächste in der Speicherreihenfolge (entsprechend einem nicht genommenen Zweig) oder der am Verzweigungsziel (entsprechend einem genommenen Zweig). Ein Verzweigungsprädiktor hilft dem Prozessor, intelligent abzuschätzen, ob eine Verzweigung genommen wird oder nicht. Dies geschieht durch das Sammeln von Statistiken darüber, wie oft bestimmte Zweige in der Vergangenheit genommen wurden.

Moderne Branch Prädiktoren sind äußerst anspruchsvoll und können sehr genaue Vorhersagen generieren. Die zusätzliche Leistung von Raspberry Pi 3 ist teilweise eine Folge von Verbesserungen in der Verzweigungsvorhersage zwischen Cortex-A7 und Cortex-A53. Durch die Ausführung einer selbstgemachten Abfolge von Zweigen kann ein Angreifer einen Verzweigungsvorhersager jedoch falsch trainieren, um schlechte Vorhersagen zu treffen.

(ID:45118037)