Frage zu Meltdown



  • Ich hab' mir jetzt das Paper zu Meltdown durchgelesen und verstehe denke ich die Beschreibung soweit. Was mir nicht ganz klar ist, ist warum die CPU den "permission check" auf die Adresse nicht macht bevor der eigentliche Memory-Load gemacht wird. Im dem Paper steht (Abschnitt 7.1):

    Serializing the permission check and the register fetch can prevent Meltdown, as the memory address is never fetched if the permission check fails. However, this involves a significant overhead to every memory fetch, as the memory fetch has to stall until the permission check is completed.

    Und ich verstehe nicht ganz warum das nen "significant overhead" haben sollte.

    Meine "Milchmädchenrechnung":

    - Die physikalische Adresse muss bekannt sein bevor etwas aus dem Hauptspeicher in den Cache geladen werden kann
    - Sollte der Speicherbereich bereits gecached sein funktioniert die Attecke nicht, d.h. virtually vs. physically indexed/tagged sollte egal sein
    - Um an die physikalische Adresse zu kommen muss ein TLB Lookup gemacht werden bzw. falls dieser fehlschlägt der ein echter Page-Table Lookup
    - Im Page Table (und ich nehme an auch im TLB - wüsste zumindest nicht wie es anders gehen sollte) steht neben der Adresse, direkt im selben "Wort", auch das "U" bit. Welches angibt ob die Seite aus dem Usermode gelesen werden darf oder nicht

    Und wenn man das Permission-Bit im gleichen Schritt bekommt wie die physikalische Adresse, kann man das Permission-Bit doch auch genauso gut checken bevor man anfängt die Cacheline zu laden.

    Ich gehe erstmal davon aus dass der Angriff wie beschrieben funktioniert (habe keinen Grund hier eine grosse Verschwörung anzunehmen). Das würde bedeuten dass zumindest auf Intel CPUs der "permission check" erst gemacht wird nachdem das Laden der Cacheline schon angestossen wurde. Ich verstehe aber nicht warum es einen "significant overhead" haben sollte es anders zu machen. Dass das vermutlich nicht mit einem Microcode Update zu beheben sein wird ist auch klar, aber zukünftigen CPU Generationen müsste man das doch beibringen können.

    Frage: Hab ich da nen Denkfehler und/oder etwas nicht richtig verstanden?



  • hustbaer schrieb:

    Und wenn man das Permission-Bit im gleichen Schritt bekommt wie die physikalische Adresse, kann man das Permission-Bit doch auch genauso gut checken bevor man anfängt die Cacheline zu laden.

    Damit führst du den Check aber gegen den Zustand zum Zeitpunkt des Ladens in den Cache aus. Dann musst du wieder sicherstellen, dass sich bis zum Zeitpunkt des Ausführen des Read Befehls das Ergebnis nicht ändert oder den Check erneut ausführen. Aber keine Ahnung wie Aufwändig das Ganze dann wird.



  • hustbaer schrieb:

    Dass das vermutlich nicht mit einem Microcode Update zu beheben sein wird ist auch klar, aber zukünftigen CPU Generationen müsste man das doch beibringen können.

    Mich würde ja interessieren, wie lange Intel dazu benötigen wird, eine neue CPU Generation ohne diesen Bug auf den Markt zu bringen.

    In folgendem Artikel steht jedenfalls folgendes:

    A fundamental flaw in the architecture will cost you five years and hundreds of millions of dollars2.

    https://danluu.com/hardware-unforgiving/

    Auch dazu lesenswert wäre:
    https://danluu.com/cpu-bugs/

    5 Jahre wären jedenfalls eine lange Zeit, wenn sich der Bug als so gravierend herausstellen sollte.
    Wenn nur ein einzelner Abschnitt in der CPU gefixed werden muss, geht's vemurtlich schneller.

    Momentan ist es bei mir jedenfalls so, dass mein leistungsfähigster vollwertiger Computer (also ohne Tablets und Smartphones gerechnet), der vermutlich nicht von diesem Bug betroffen ist, mein Raspberry Pi 3 für ca. 35 € ist.

    Mein i7-6700K ist davon jedenfalls betroffen.



  • Korrektur:

    Das mit dem Raspberry Pi 3 nehme ich zurück. Der ist zwar sicher gegen Meltdown, aber gegen Spectre scheinen auch die ARM CPUs anfällig zu sein.

    Ich habe aber noch einen 486er, der ist sicher gegen beide Angriffsarten. 🙂



  • Tobiking2 schrieb:

    hustbaer schrieb:

    Und wenn man das Permission-Bit im gleichen Schritt bekommt wie die physikalische Adresse, kann man das Permission-Bit doch auch genauso gut checken bevor man anfängt die Cacheline zu laden.

    Damit führst du den Check aber gegen den Zustand zum Zeitpunkt des Ladens in den Cache aus. Dann musst du wieder sicherstellen, dass sich bis zum Zeitpunkt des Ausführen des Read Befehls das Ergebnis nicht ändert oder den Check erneut ausführen. Aber keine Ahnung wie Aufwändig das Ganze dann wird.

    Ah, ja, stimmt.

    Ich denke das sollte sich aber mit vertretbarem Overhead fixen lassen. Im Prinzip braucht man ja nur ne Dependency zwischen Kernel-Memory Zugriffen und Befehlen wie syscall & Co.



  • computertrolls schrieb:

    hustbaer schrieb:

    Dass das vermutlich nicht mit einem Microcode Update zu beheben sein wird ist auch klar, aber zukünftigen CPU Generationen müsste man das doch beibringen können.

    Mich würde ja interessieren, wie lange Intel dazu benötigen wird, eine neue CPU Generation ohne diesen Bug auf den Markt zu bringen.

    Um Meltdown zu fixen ist sicher keine grundlegend neue Microarchitektur nötig.

    Bei Spectre könnte es schwieriger werden. Falls man für Spectre allerdings immer den Branch-Target-Buffer braucht, um die Speculative-Execution im Ziel-Process an eine bestimmte Stelle zu lenken, dann sollte das IMO auch relativ leicht dadurch fixbar sein dass man jedem Hardware Thread einen eigenen Branch-Target-Buffer spendiert.

    Aber um Spectre geht's mir erstmal nicht, dazu möchte ich erst noch mal das Paper lesen und mir in Ruhe ein paar Gedanken dazu machen. Weswegen ich hier ja auch konkret nach Meltdown gefragt habe und nicht nach Spectre.



  • Tobiking2 schrieb:

    hustbaer schrieb:

    Und wenn man das Permission-Bit im gleichen Schritt bekommt wie die physikalische Adresse, kann man das Permission-Bit doch auch genauso gut checken bevor man anfängt die Cacheline zu laden.

    Damit führst du den Check aber gegen den Zustand zum Zeitpunkt des Ladens in den Cache aus. Dann musst du wieder sicherstellen, dass sich bis zum Zeitpunkt des Ausführen des Read Befehls das Ergebnis nicht ändert oder den Check erneut ausführen. Aber keine Ahnung wie Aufwändig das Ganze dann wird.

    Verstehe den Einwand nicht. Wenn sich die page table während des Ladens ändert (wie soll das gehen?) wäre ja auch die physikalische Adresse hinfällig.
    Wenn sich der Inhalt des Speichers ändert, ist das von der physikalischen Adresse und permission unahbhängig und ein anderes Problem.

    Noch ne andere Frage zu Meltdown:
    Wozu wird zunächst eine exception generiert?
    Wenn erst die Speicherstelle gelesen und dann die permission geprüft wird, muss das Ganze doch auch direkt funktionieren?
    Was hat das mit instruction reordering zu tun?



  • C14 schrieb:

    Verstehe den Einwand nicht. Wenn sich die page table während des Ladens ändert (wie soll das gehen?) wäre ja auch die physikalische Adresse hinfällig.
    Wenn sich der Inhalt des Speichers ändert, ist das von der physikalischen Adresse und permission unahbhängig und ein anderes Problem.

    Die CPU kann dazwischen in den Supervisor Mode wechseln oder aus dem Supervisor Mode zurück in den User Mode.

    C14 schrieb:

    Noch ne andere Frage zu Meltdown:
    Wozu wird zunächst eine exception generiert?

    Beliebig. z.B. Division durch null. Kann sich der Angreifer aussuchen.

    C14 schrieb:

    Wenn erst die Speicherstelle gelesen und dann die permission geprüft wird, muss das Ganze doch auch direkt funktionieren?
    Was hat das mit instruction reordering zu tun?

    Mit Speculative Execution und dem Cache hat es zu tun. Is aber a bissi viel um es in an Forums-Posting zu erklären. Lies das Paper: https://meltdownattack.com/meltdown.pdf



  • C14 schrieb:

    Wozu wird zunächst eine exception generiert?

    Soweit ich es verstanden habe erhöht die Exception die Zeit zwischen dem Read und dem Permission Check. Google gibt als Alternative eine Branch-Misprediction an. Ist aber nicht so leicht zu generieren. Grundsätzlich muss der Zeitraum muss groß genug sein um ein weiteren Read auszuführen.

    C14 schrieb:

    Wenn erst die Speicherstelle gelesen und dann die permission geprüft wird, muss das Ganze doch auch direkt funktionieren?
    Was hat das mit instruction reordering zu tun?

    Der grundlegende Trick ist:

    X = Lese Wert aus Kernelspace
    Y = Lese Wert aus A + X * Cacheline
    Für i=0...
      Messe Zeit für Zugriff auf A + i * Cacheline
      Wenn Zeit == Cachezugriff => X = i
    

    Und das geht nur wenn die Abfolge der Befehle intern folgendermaßen aussieht:

    1. Lese Wert aus Kernelspace
    2. Lese Wert aus A + X * Cacheline
    3. Prüfe ob 1. erlaubt war => Abbruch von 1. bzw. Verwerfen des Ergebnisses

    Würde 3. vor 2. passieren würde A + X * Cacheline nicht im Cache landen.



  • Oh ok, man braucht ja beide Instruktionen 🙄
    Danke für die gute Zusammenfassung, Tobiking2!



  • Wie lang braucht man dann, um irgendwas sinnvolles damit auszulesen?
    Wenn man das Video hier anschaut bei ca. 5:45, dann ist das relativ langsam für ein paar Wörter.
    https://www.youtube.com/watch?v=I5mRwzVvFGE

    Das würde ja ewig dauern, da ein MB oder GB auszulesen. Gibt es dazu Werte?



  • Zeit??? schrieb:

    Wie lang braucht man dann, um irgendwas sinnvolles damit auszulesen?
    Wenn man das Video hier anschaut bei ca. 5:45, dann ist das relativ langsam für ein paar Wörter.
    https://www.youtube.com/watch?v=I5mRwzVvFGE

    Das würde ja ewig dauern, da ein MB oder GB auszulesen. Gibt es dazu Werte?

    Im Paper steht etwas von 503 kb/s maximal. Gezeigte Proof of Concepts zu Spectre und Meltdown haben aber teilweise auch nur 1-2 kb/s. Und je nach Variante muss der richtige Speicherbereich noch gesucht werden, was mit 10-30 Minuten (bei 64 gb RAM) angegeben ist.

    Es ist schon nur dafür geeignet gezielt Passwörter oder Private Keys auszulesen.



  • Man muss doch für jedes bit, das man auslesen will, immer wieder den Cache flushen. Sollte ja relativ einfach sein, die Anzahl der Cache flushes zu zählen und wenn ein Programm extrem viele macht, wird es vom OS beendet.



  • SW Fix schrieb:

    Man muss doch für jedes bit, das man auslesen will, immer wieder den Cache flushen.

    Du musst nicht den ganzen Cache flushen - es reicht die Cache-Lines rauszuwerfen die du für die Überprüfung brauchst. Bei nem einzelnen Bit sind das bloss zwei bzw. evtl. sogar nur eine.
    Bei einem 16-fach assoziativ Cache wie von z.B. Skylake müsstest du dazu bloss 32 bzw. 16 andere Cache-Lines laden. Sollte schneller gehen als den ganzen Cache wegzuwerfen.

    SW Fix schrieb:

    Sollte ja relativ einfach sein, die Anzahl der Cache flushes zu zählen und wenn ein Programm extrem viele macht, wird es vom OS beendet.

    Nur dass man den Cache nicht flushen muss, siehe oben. Bzw. selbst wenn man müsste, dann wäre das etwas was vielleicht ein Virenscanner bei entsprechend paranoiden Einstellungen machen könnte, aber sicher nicht das OS. Ich hätte zumindest keine Freude damit wenn das OS einfach so beschliesst Programme zu killen - trotz dem diese nichts explizit verbotenes machen.
    (BTW: Ich weiss nichtmal ob die zum Flushen des Cache nötigen Befehle überhaupt im Usermode ausgeführt werden dürfen. Ich gehe einfach mal davon aus dass ja, denn wenn nein, dann ist die Überlegung sowieso hinfällig. Denn dann bleibt sowieso nur das Überschreiben mittels Laden von anderen Cache-Lines.)



  • Tobiking2 schrieb:

    Im Paper steht etwas von 503 kb/s maximal.

    ~500 kB/s mit Meltdown auf CPUs die Hardware Transactional Memory unterstützen.
    Ansonsten mit Meltdown ca. 1/4 davon.

    Tobiking2 schrieb:

    Gezeigte Proof of Concepts zu Spectre und Meltdown haben aber teilweise auch nur 1-2 kb/s.

    Mit Spectre wird man viel viel langsamer sein. Bei Meltdown sollte man aber auf die angegebenen Werte kommen - die haben sich die Jungs ja schliesslich nicht aus den Fingern gesaugt.


Log in to reply