Linux 5.14 mit "geheimem" Speicher und sicherem Hyperthreading

Seite 2: Core-Scheduling gegen Spectre

Inhaltsverzeichnis

Nach über drei Jahren Entwicklungszeit ist das sogenannte Core-Scheduling im Mainline-Kernel angekommen. Es adressiert ein konzeptionelles Problem des Hyperthreading moderner Prozessoren, das durch die 2018 entdeckten Spectre-Angriffe zutage trat.

Hyperthreading oder "Simultaneous Multithreading" (SMT) ermöglicht das Ausführen zweier oder mehr Threads auf einem Prozessorkern (Core). Der Core zeigt sich nach außen wie ein abgespeckter Mehrkernprozessor. "Abgespeckt" deshalb, da immer nur ein Thread aktiv sein kann und läuft – echte Parallelverarbeitung wie bei einem Multicore beherrscht Hyperthreading nicht. Und dennoch ist es sinnvoll, da Wartezeiten einzelner Threads von anderen Threads genutzt, "Leerläufe" reduziert und CPU-Ressourcen somit effizienter genutzt werden können.

Beim Hyperthreading nutzen die Hardware-Threads Komponenten des Prozessor-Cores gemeinsam – vor allem die Prozessor-Cache-Speicher. An dieser Stelle setzen Spectre und verwandte Angriffe an. Ein bösartiger Prozess auf einem Thread kann auf Änderungen in den Caches lauern. Zu diesen Änderungen können etwa Daten anderer Threads zählen, deren Prozesse sich auf diese Weise ausspähen lassen. Fährt ein System sehr unterschiedliche Lasten aus unterschiedlichen Quellen, wie beispielsweise ein Cloud-Provider, entsteht schnell eine Sicherheitsgrauzone. Der einzige wirklich sichere Ausweg war bislang, Hyperthreading zu deaktivieren.

Core-Scheduling bietet für das Problem nun eine bessere Lösung. Es lässt nur Prozesse gemeinsam in Hardware-Threads eines Core laufen, die die gleiche Vertrauensstufe besitzen. Bezogen auf das Beispiel des Cloud-Providers werden Cluster von Kunden so aufgeteilt, dass sie sich keine Cores im Hyperthreading teilen: Prozesse eines Kunden laufen nur mit Prozessen desselben Kunden auf einem Core zusammen. Die Kunden können sich so gegenseitig nicht ausspionieren und Hyperthreading lässt sich dennoch nutzen.

Intern führt der Kernel hierzu "Cookies" ein. Diese Cookies weist das Core-Scheduling den Prozessen zu und markiert diese so als zueinander gehörig. Prozesse mit identischen Cookies dürfen auf denselben Cores im Hyperthreading laufen, "vertrauen" sich also. Aus den lauffähigen Prozessen, den verfügbaren Cores und deren Threads sowie den Cookie-Gruppen berechnet das Core-Scheduling dann passende Abläufe und die Verteilung der Prozesse auf die Cores/Threads.

Implementiert ist die API zum Core-Scheduling in der Sammlung verschiedenster Operationen für Prozesse und Threads von prctl(). Über die hinzugekommene Option PR_SCHED_CORE lassen sich Cookies erzeugen und diesen Prozessen zuweisen. Beim Erzeugen ist ein Prozess als "Blaupause" anzugeben. Dieser Prozess erhält den neuen Cookie. Das Zuweisen an andere Prozesse erfolgt dann über das "Auslesen" und Zuweisen desselben Cookies an einen anderen Prozess. Im Kernel sind derweil mehrere Sicherheitsmaßnahmen aktiv, damit sich kein Prozess einen Cookie erschleichen kann und Cookies stets eindeutig sind.

Während des Scheduling ändert sich mit dem neuen Feature initial nichts: Ein Prozessor wird unabhängig von Cookies mit dem höchst priorisierten Prozess aus der Run-Queue zum Ausführen bestückt. Bei aktivem Core-Scheduling sendet der Core danach jedoch ein Interprozesssignal an seine Thread-Ausführungseinheiten. Jede Thread-Einheit prüft daraufhin, ob der Cookie des eigenen Prozesses mit dem des soeben bestückten Prozesses des aktiven Threads übereinstimmt. Ist das der Fall, so behält der Thread seinen Prozess und wartet aufs Aktivieren. Stimmen die Cookies hingegen nicht überein, erfolgt ein Switch zu einem Prozess mit passendem Cookie. Ist kein Prozess mit passendem Cookie in der Run-Queue vorhanden, geht der Thread in den Wartezustand statt einen anderen, nicht vertrauenswürdigen Prozess auszuführen.

Dem Grundgedanken und der Effizienz des Hyperthreading läuft dieser Wechsel in den Wartemodus trotz lauffähiger Prozesse in der Run-Queue zuwider. Dennoch ist die Core-Auslastung dabei immer noch besser als bei einer kompletten Deaktivierung des Hyperthreading. Core-Scheduling ist somit ein guter Kompromiss zwischen optimierter Hardware-Auslastung und Sicherheitsanspruch.

Der Einsatz des Core-Scheduling kann nicht nur die Sicherheit erhöhen, sondern auch in anderen Bereichen nützlich sein – etwa bei Echtzeitsystemen, bei denen Hyperthreading typischerweise ausgeschaltet ist. Denn Echtzeitverarbeitung ist nur dann möglich, wenn ein Prozessorkern ausschließlich dem Echtzeitprozess zur Verfügung steht. Quer treibende Threads können dies unterlaufen. Mit Core-Scheduling können Echtzeitprozesse über dedizierte Cookies einzelnen Cores fix zugewiesen werden. Wenn es keine anderen Prozesse mit gleichen Cookies gibt, gehen die anderen Threads des jeweiligen Kerns dauerhaft in den Wartestatus. Jene Prozesse, die nicht in Echtzeit verarbeiten müssen, können sich Cookies und somit auch Cores im Hyperthreading teilen.