Suchen

Hacker: Warum Raspberry Pi sicher vor Meltdown und Spectre ist

Seite: 2/3

Firmen zum Thema

Warum vorausschauende Zugriffe so gefährlich sind

Das Umordnen sequentieller Anweisungen ist ein mächtiger Weg, um mehr Parallelität auf Befehlsebene wiederherzustellen, aber wenn die Prozessoren breiter werden (in der Lage, Befehle dreimal oder vierfach auszugeben), wird es schwieriger, all diese Pipes beschäftigt zu halten. Moderne Prozessoren haben daher die Fähigkeit ausgebildet, zu spekulieren. Durch die spekulative Ausführung können wir Anweisungen ausgeben, die sich als nicht erforderlich erweisen (weil sie verzweigt werden können): Dadurch bleibt eine Pipe beschäftigt (benutzen Sie sie oder verlieren Sie sie!), Und wenn sich herausstellt, dass die Anweisung nicht ausgeführt wird – können wir das Ergebnis einfach fallen lassen.

Das Ausführen unnötiger Anweisungen (und der Infrastruktur, die zur Unterstützung von vorausschauenden Zugriffen und Neuanordnungen erforderlich ist) verbraucht zusätzliche Energie. In vielen Fällen wird dies jedoch als lohnenswerte Abwägung betrachtet, um zusätzliche Single-Thread-Leistung zu erzielen. Der Verzweigungsprädiktor wird verwendet, um den wahrscheinlichsten Pfad durch das Programm zu wählen, wodurch die Wahrscheinlichkeit maximiert wird, so dass sich vorausschauende Zugriffe auszahlen.

Bildergalerie

Bildergalerie mit 8 Bildern

Um die Vorteile der vorausschauenden Zugriffe zu demonstrieren, schauen wir uns ein anderes Beispiel an:

t = a + b

u = t + c

v = u + d

wenn v:

w = e + f

x = w + g

y = x + h

Jetzt haben wir Abhängigkeiten von t zu u zu v und von w zu x zu y, so dass ein Zwei-Wege-Out-of-Order-Prozessor ohne Spekulation seine zweite Pipe niemals füllen kann. Er gibt drei Zyklen aus, um t, u und v zu berechnen, nach denen es weiß, ob der Block der if-Anweisung ausgeführt wird, in welchem ​​Fall es dann drei Zyklen aufwendet, um w, x und y zu berechnen. Unter der Annahme, dass if (implementiert durch einen Verzweigungsbefehl) einen Zyklus benötigt, nimmt unser Beispiel entweder vier Zyklen (wenn v zu Null wird) oder sieben Zyklen (wenn v nicht null ist) an.

Wenn der Zweigprädiktor anzeigt, dass der Körper der if-Anweisung wahrscheinlich ausgeführt wird, mischt ein vorausschauendes Lesen das Programm folgendermaßen:

t = a + b

u = t + c

v = u + d

w_ = e + f

x_ = w_ + g

y_ = x_ + h

wenn v:

w, x, y = w_, x_, y_

So haben wir jetzt zusätzliche Parallelität auf Befehlsebene, um unsere Pipes beschäftigt zu halten:

t, w_ = a + b, e + f

u, x_ = t + c, w_ + g

v, y_ = u + d, x_ + h

wenn v:

w, x, y = w_, x_, y_

Die Zykluszählung wird in spekulativen Out-of-Order-Prozessoren weniger gut definiert, aber die Verzweigung und die bedingte Aktualisierung von w, x und y sind (ungefähr) frei, so dass unser Beispiel in (ungefähr) drei Zyklen ausgeführt wird.

Was ist ein Cache?

In früheren Zeiten war die Geschwindigkeit der Prozessoren gut mit der Geschwindigkeit des Speicherzugriffs abgestimmt. Mein BBC Micro mit seinem 2 MHz 6502 konnte ungefähr alle 2 μs (Mikrosekunden) einen Befehl ausführen und hatte eine Speicherzykluszeit von 0,25 μs. In den folgenden 35 Jahren sind die Prozessoren sehr viel schneller geworden, aber Speicher ist bescheiden: Ein einzelnes Cortex-A53 in einem Raspberry Pi 3 kann einen Befehl ungefähr alle 0,5 ns (Nanosekunden) ausführen, aber es kann bis zu 100 ns dauern, um Zugriff auf den Hauptspeicher zu erhalten.

Auf den ersten Blick klingt das wie eine Katastrophe: Jedes Mal, wenn wir auf Speicher zugreifen, warten wir bis zu 100 ns, um das Ergebnis zurück zu bekommen. In diesem Fall, dieses Beispiel:

a = mem [0]

b = mem [1]

würde 200 ns kosten.

In der Praxis tendieren Programme jedoch dazu, auf relativ vorhersehbare Weise auf den Speicher zuzugreifen, wobei sowohl zeitliche Lokalität (wenn ich auf einen Speicherplatz zugreife, werde ich wahrscheinlich bald wieder darauf zugreifen) als auch räumliche Lokalität (wenn ich auf einen Speicherplatz zugreife, greife ich in Kürze wahrscheinlich wahrscheinlich auch auf einen Speicherplatz in der Nähe zu). Caching nutzt diese Eigenschaften für den vorausschauenden Speicherzugriff.

Ein Cache ist ein kleiner On-Chip-Speicher in der Nähe des Prozessors, der Kopien der Inhalte kürzlich verwendeter Speicherorte (und ihrer Nachbarn) speichert, so dass sie bei nachfolgenden Zugriffen schnell verfügbar sind. Beim Caching wird das obige Beispiel in etwas über 100 ns ausgeführt:

a = mem [0] # 100ns Verzögerung, kopiert mem [0:15] in den Cache

b = mem [1] # mem [1] befindet sich im Cache

Aus der Sicht von Spectre und Meltdown ist es wichtig, dass Sie, wenn Sie die Speicherdauer eines Speichers zeitlich bestimmen können, feststellen können, ob die Adresse, auf die Sie zugegriffen haben, im Cache lag (kurze Zeit) oder nicht (lange Zeit).

Was ist eine Seitenkanalattacke?

"Quelle Wikipedia... Ein Seitenkanalangriff, bezeichnet eine kryptoanalytische Methode, die die physische Implementierung eines Kryptosystems in einem Gerät (z. B. einer Chipkarte, eines Security-Tokens oder eines Hardware-Sicherheitsmoduls) oder in einer Software ausnutzt. Dabei wird nicht das kryptographische Verfahren selbst, sondern nur eine bestimmte Implementierung angegriffen, d. h. andere Implementierungen können von dem Angriff unberührt bleiben. Zum Beispiel können Zeitinformationen, Stromverbrauch, elektromagnetische Lecks oder sogar Schall eine zusätzliche Informationsquelle darstellen, die ausgenutzt werden kann, um das System zu durchbrechen. "

Spectre und Meltdown sind Side-Channel-Attacken, die den Inhalt eines Speicherplatzes auslesen, auf den normalerweise nicht zugegriffen werden könnte. Dabei nutzen sie den Zeitraum, indem überprüft wird, ob ein anderer zugreifbarer Ort im Cache vorhanden ist.

(ID:45113225)

Über den Autor

 Margit Kuther

Margit Kuther

Redakteur, ELEKTRONIKPRAXIS - Wissen. Impulse. Kontakte.