Privilegienstufe

Privilegierungs- bzw. Sicherheitsstufe eines Prozesses im Umfeld der Betriebssystem-Programmierung
(Weitergeleitet von Kernelspace)

Die Privilegienstufe (engl. privilege level) bezeichnet (im Umfeld der Betriebssystem-Programmierung und des Multitaskings) eine Privilegierungs- bzw. Sicherheitsstufe des gerade laufenden Programmcodes. Es handelt sich um eine Funktion der Hardware, durch die der auf der CPU nutzbare Befehlssatz und der verwendbare Speicherbereich dynamisch eingeschränkt werden kann. Die Nutzung von Privilegierungsebenen ist sinnvoll, um die Hardware zu abstrahieren und um Prozesse voneinander und vom Betriebssystem und Treibern abzuschotten.

Je nach Prozessorarchitektur wird die Privilegienstufe auch Ring, Schutzring oder Domain genannt.

Privilegienstufen verschiedener Architekturen

Bearbeiten

Es gibt verschiedene Privilegienmodelle. Während manche Architekturen wie x86 sehr viele Privilegienstufen besitzen, existiert bei anderen Architekturen nur eine Zweiteilung in Benutzer- und Systemmodus.

 
Schema der Ringe beim x86-System mit Gates zur Kommunikation

Intel-80286-kompatible Prozessoren unterscheiden vier Privilegierungsstufen: Ring 0, 1, 2 und 3. Dabei stellt Ring 0, genannt „supervisor mode“, die höchste Privilegierungsstufe dar, die bis zur Stufe 3 (Ring 3) immer weiter eingeschränkt wird. Beispiele für Maschinenbefehle, die im Ring 0, jedoch nicht im Ring 3 ausgeführt werden dürfen, sind z. B. „cli“ und „sti“. Mit diesen Anweisungen wird die Behandlung von (maskierbaren) Hardwareinterrupts ab- bzw. eingeschaltet. Ursprünglich waren die Ringe für den Kernel (Ring 0), Treiber (Ring 1), Systemdienste (Ring 2) und Anwendungsprogramme (Ring 3) vorgesehen.

Um Prozesse in einem geschützten Bereich (Ring > 0) ablaufen zu lassen, wird der physikalische Arbeitsspeicher in virtuelle Speicherseiten aufgeteilt. Zu jeder Speicherseite existiert eine Tabelle, in der unter anderem gespeichert ist, in welchem Level (Ring) der Programmcode, der innerhalb dieser Speicherseite gespeichert ist, ausgeführt wird. Diese Auswertung nimmt die MMU meist extern vor.

Wechsel zwischen den Ringen

Bearbeiten

Für einen Wechsel des Rings stehen drei Gate-Typen zur Verfügung, die bei ihrer Verwendung unterschiedlich viel Rechenzeit in Anspruch nehmen, da jeder Wechsel von einem Ring zum anderen auch einen Kontextwechsel zumindest einiger Zustände in der CPU darstellt:

  1. Call-Gates für den direkten Aufruf von Programmcode aus höheren Privilegierungsebenen. Das Call-Gate bestimmt dabei, an welcher Stelle und mit welchen Privilegien der aufgerufene Programmcode laufen wird. Aus Sicherheitsgründen wird hierbei dem Programmcode aus der höheren Privilegierungsebene ein eigener Stapel zugewiesen, die Aufrufparameter vom Stapel des aufrufenden Codes werden in den neuen Stapel kopiert. Im Übrigen läuft der privilegierte Code im Kontext des aufrufenden Codes.
  2. Interrupt-Gates werden zum einen beim Auslösen eines so genannten Software-Interrupts verwendet, aber auch Hardware-Interrupts erfordern ein Interrupt-Gate. Zusätzlich zu allen Schritten, die beim Benutzen eines Call-Gates durchgeführt werden, wird zusätzlich das Flags-Register auf dem Stapel gespeichert und weitere Interrupts bis zur Rückkehr der Interrupt-Routine gesperrt.
  3. Task-Gates erlauben die Kontrolle an einen anderen Prozess abzugeben. Dies stellt die aufwändigste Form eines Kontextwechsels dar, da hier der vollständige Prozessorzustand des aufrufenden Prozesses gespeichert und des aufgerufenen Prozesses geladen werden muss.

Betriebssysteme auf x86

Bearbeiten

Die verbreiteten Betriebssysteme für x86 Linux und Windows (sowie macOS für x64 und DOS mit EMM386.EXE-Speichermanager) nutzen lediglich zwei der vier möglichen CPU-Ringe. Im Ring 0 werden der Kernel und alle Hardwaretreiber ausgeführt, während die Anwendungssoftware im unprivilegierten Ring 3 arbeitet. Damit bleibt die Portabilität des Betriebssystems auch auf Prozessorarchitekturen gewährleistet, die nur zwei Privilegierungsstufen unterscheiden können. OS/2 benutzt allerdings Ring 2 für Grafiktreiber.[1] Eine speziell angepasste Version des Speichermanagers EMM386 aus dem Entwicklungskit für DOS Protected Mode Services (für Novell DOS 7, OpenDOS 7.01 und DR-DOS 7.02 und höher) lässt DPMS auf Ring 1 statt auf Ring 0 laufen und erleichtert so die Fehlersuche bei Software, die DPMS nutzt.

Manche Virtualisierungslösungen verwenden auch Ring 1. Hierbei wird der Betriebssystemkern aus Ring 0 in Ring 1 verschoben, der Hypervisor residiert dann als darüberliegende Schicht in Ring 0 und verwaltet einen oder mehrere in Ring 1 laufende Betriebssystemkerne. Dies kann allerdings auch durch Rootkits ausgenutzt werden, um Schadcode unbemerkt vom Anwender auf dem Ring 0 ausführen zu lassen (siehe auch Virtual Machine Based Rootkit).

Ring 0 (Kernel-Mode)

Bearbeiten

Im innersten Ring (höchste Berechtigungsstufe; privilegierte Ebene; System-Ebene; Kern-Ebene) läuft meist das Betriebssystem, evtl. sogar nur dessen Kernel und damit dessen Bestandteile wie Prozessverwaltung, Speicherverwaltung, Geräteverwaltung, Dateisysteme und Schnittstellen zur Hardware. Das Betriebssystem „darf alles“, insbesondere direkte Hardwarezugriffe und das Eingreifen in die RAM-Bereiche anderer Prozesse. Je nach Aufbau des Kernels werden Treiber auch in Ring 0 ausgeführt, was bei den meisten Betriebssystemen der Fall ist.

Code auf dieser Ebene darf...

Ring 1 und 2

Bearbeiten

Diese Ringe sind zwischen Kernel- und Benutzermodus. Programme, die in dieser Stufe laufen, dürfen daher zwar weniger als der Kernel, aber mehr als Programme im Benutzermodus. Da diese Ringe jedoch in vielen anderen Prozessorarchitekturen nicht vorhanden sind, werden sie bei den meisten Betriebssystemen nicht genutzt – so verwenden z. B. diverse Unix-Systeme, Windows NT und Linux dasselbe Kernel-Design auf einer Vielzahl unterschiedlicher Befehlssatzarchitekturen, von denen die meisten nur einen privilegierten Modus (Kernelmodus, bei x86 Ring 0) und einen unprivilegierten Modus (Benutzermodus, bei x86 Ring 3) bieten.

Ein Beispiel für ein Betriebssystem, das Gerätetreiber im Ring 2 ausführt, ist OS/2 auf der 32-Bit-x86-Architektur IA-32.[1]

Ring 3 (User-Mode)

Bearbeiten

Anwendungsprogramme sind üblicherweise auf den äußersten Ring beschränkt (niedrigste Berechtigungsstufe). Für Operationen, welche einen Hardwarezugriff erfordern, müssen Anwendungsprogramme Betriebssystem-Dienste beauftragen.

Ring −1 (Hypervisor-Modus)

Bearbeiten

Um die Verwendung von Hypervisoren zu vereinfachen, führen neuere CPUs von Intel und AMD einen neuen „Ring –1“ ein, so dass der Betriebssystemkern in Ring 0 verbleibt, während der Hypervisor als darüberliegende Schicht in Ring –1 residiert. Dabei verwaltet er einen oder mehrere Betriebssystemkerne in Ring 0.

Ring −2 (Systemverwaltungsmodus)

Bearbeiten

Da moderne Systeme immer komplexer wurden, begannen CPUs ab dem Intel 386SL einen Systemverwaltungsmodus zu implementieren, der, firmwareähnlich, grundlegende „interne“ Hardwarefunktionalität implementierte. Dieser wird unter anderem für Systemereignisse, Temperatur- und Energieverwaltung, SMBIOS, ACPI und Sicherheitsfunktionen verwendet.

Ring −3 (Intel Management Engine / AMD Platform Security Processor)

Bearbeiten

Seit 2008 besitzen moderne Computer zudem ein unsichtbares, vollständig unabhängiges System mit nicht genau bekannter Funktionalität, mit noch höheren Privilegien, das selbst im „ausgeschalteten“ Zustand weiterläuft. Auf Intel-Systemen ist dies die Intel Management Engine, und auf AMD-Systemen der Platform Security Processor.

Auf den ARM-Architekturen werden die Privilegienstufen als Exception Levels, d. h. Ausnahmestufen, bezeichnet. Dies ist darin begründet, dass der Übergang zwischen Privilegienstufen nur mittels Auftreten einer Ausnahme oder Rückkehr von einer Ausnahmebehandlung möglich ist.[2] Auf AArch64 (Armv8 und später) gibt es vier Exception Levels, die mit EL0 bis EL3 bezeichnet werden. Dabei ist EL0 das unprivilegierte Exception Level und die anderen Exception Levels sind privilegiert. Die virtuelle Speicherverwaltung weist den Speicherbereichen entsprechend privilegierte (für Zugriffe aus EL1-EL3) und unprivilegierte (für Zugriffe aus EL0) Zugriffsrechte zu. Jedem Systemregister ist ein kleinstes Exception Level zugewiesen, aus welchem der Zugriff auf dieses erlaubt ist. Dies ist auch im Registernamen angegeben, so beispielsweise VBAR_EL1 für die Startadresse der Ausnahmevektortabelle, auf die nur aus Exception Level 1 und höher zugegriffen werden kann.

Je nach Implementierung können EL2 und EL3 auch nicht implementiert sein, nur EL0 und EL1 sind verpflichtend auf allen ARM-Plattformen. Falls EL3 nicht implementiert ist, besitzt der Prozessor einen nicht änderbaren Sicherheitszustand, da dieser nur aus EL3 geändert werden könnte.

Exception Level 0

Bearbeiten

EL0 ist für Anwendungsprogramme vorgesehen. Der Zugriff auf die meisten Systemregister ist aus EL0 nicht möglich. EL0 teilt sich die MMU-Konfiguration mit EL1. Um Funktionen des Betriebssystems abzurufen (Systemaufruf), kann ein Programm unter EL0 eine synchrone Ausnahme mittels svc (Supervisor Call) auslösen, welche den Prozessor in EL1 versetzt und die EL1-Ausnahmebehandlung aufruft.

Exception Level 1

Bearbeiten

EL1 ist für Betriebssysteme vorgesehen. EL1 kann seine eigene MMU-Konfiguration (entsprechend der MMU-Konfiguration von EL0) ändern und auf Systemregister zugreifen, die EL1 und EL0 beeinflussen. Zur Kommunikation mit einem Hypervisor in EL2 dient der hvc-Befehl.

Exception Level 2

Bearbeiten

EL2 ist für Hypervisoren vorgesehen. EL2 hat Zugriff auf Konfigurationen zur Virtualisierung mehrerer Betriebssysteme. Es können Aufrufe an den Secure Monitor (via smc) aus EL1 abgefangen werden, um beispielsweise einen Secure Monitor zu emulieren.

Exception Level 3 (ARM TrustZone)

Bearbeiten

EL3 ist für Firmware oder einen Secure Monitor vorgesehen. Aus EL3 kann der Sicherheitsmodus des Prozessors umgeschaltet werden, sodass Betriebssysteme und Hypervisoren verwaltet werden können, die im sicheren Modus ausgeführt werden (sogenannte ARM TrustZone). EL3 selbst wird (außer auf manchen Armv9-Systemen) immer im sicheren Modus ausgeführt. Ist der sichere Modus implementiert, kann Software aus EL2 Funktionen des Secure Monitor mittels des smc-Befehls (Secure Monitor Call) abrufen.

Der Privilege Mode, oft einfach Mode, von RISC-V hat drei Stufen, genannt U-Mode, S-Mode und M-Mode. Dabei müssen nicht alle Modi implementiert sein; die folgenden Konfigurationen sind möglich:

Implementierte Modi Vorgesehen für
M einfache Embedded-Systeme
M, U sichere Embedded-Systeme
M, S, U übliche Betriebssysteme

Dazu kommt die Hypervisor-Erweiterung, die U- und S-Mode in VU, HU, VS und HS aufteilt. Die virtuellen Modi werden von virtualisierten Betriebssystemen genutzt, während die Hypervisor-Modi vom Hypervisor-Betriebssystem und seinen Anwendungsprogrammen genutzt wird. Auch der Debug-Modus einer debugbaren Implementierung ist streng genommen ein eigener Privilegienmodus, der Zugriff auf Register und Speicherbereiche hat, die aus dem M-Mode nicht benutzt werden können.

Die Kommunikation zwischen den Modi geschieht mittels Umgebungsaufrufen durch den ecall-Befehl, was jeweils eine Anfrage an den nächstniedrigeren Modus stellt. Ein Umgebungsaufruf vom U-Mode an den S-Mode ist also ein Systemaufruf, ein Umgebungsaufruf vom S-Mode an den M-Mode ein Aufruf an die Firmware, und ein Umgebungsaufruf vom VS-Mode an den HS-Mode ein Aufruf an virtuelle Firmware, also ein Hypercall bzw. Hypervisor-Aufruf. Außerdem werden Interrupts von höheren Modi behandelt. Standardmäßig behandelt M-Mode alle Interrupts, die Behandlung einzelner Interrupts kann von dort an S-Mode abgegeben werden.

Der U-Mode (User/Application) ist für Anwendungsprogramme gedacht, was auch die Anwendungssoftware in sicheren Embedded-Systemen einschließt. U-Mode hat keinen Zugriff auf Kontroll- und Statusregister (CSRs), die Hardwarefunktionen konfigurieren, und unterliegt (falls implementiert) der virtuellen Speicherverwaltung, sodass der Zugriff auf Speicherbereiche eingeschränkt ist. Es ist nicht möglich, Interrupts im U-Mode zu behandeln.

S-Mode (Supervisor) ist für Betriebssysteme gedacht. S-Mode kann jeden Speicher aller Programme lesen und schreiben, einen Großteil des Systems konfigurieren, sofern M-Mode dies zulässt, und Traps (Interrupts und Ausnahmen) behandeln. Auch ein Hypervisor wird im S-Mode ausgeführt, dieser kann zusätzlich auf die CSRs zur Konfiguration der virtualisierten Systeme zugreifen und ihren Speicher lesen. S-Mode hat keinen Zugriff auf PMP (Physical Memory Protection), was es M-Mode erlaubt, bestimmte Speicherbereiche auch vor dem Betriebssystem abzusichern.

M-Mode (Machine) ist für Firmware gedacht, bzw. für die Software in sehr einfachen Embedded-Systemen oder Mikrocontrollern, die effektiv keine Privilegienmodi besitzen. M-Mode hat als einziger Modus uneingeschränkt Zugriff auf alle Speicherbereiche und CSRs. Eine CPU wird im M-Mode gestartet, und die Firmware übergibt im Laufe des Bootprozesses die Kontrolle an ein Betriebssystem im S-Mode. Standardmäßig werden alle Traps im M-Mode behandelt, was z. B. die Emulation fehlender Befehle oder Hardwarefunktionen in Firmware erlaubt. M-Mode kann allerdings bestimmte Traps an S-Mode-Software delegieren (mittels mideleg und medeleg), beispielsweise für die virtuelle Speicherverwaltung. In der Regel ist der Code, der im M-Mode ausgeführt wird, nicht veränder- oder beeinflussbar, ähnlich wie IME oder PSP.

Die bei x86-Prozessoren vorgenommene Einteilung in vier Privilegienstufen bzw. Ringe wurde schon früher eingesetzt, z. B. bei der VAX. Der Alpha-Prozessor unterstützt einen zusätzlichen geschützten Bereich für dessen PAL-Code, welcher mit Ring −1 auf x86 oder M-Mode auf RISC-V vergleichbar ist. Die Honeywell 6180, das erste System mit Hardware-Unterstützung für dieses Konzept, kannte acht Privilegienstufen.

Umsetzung

Bearbeiten

Der Befehlssatz wird für unprivilegierte Prozesse (userspace oder userland) derart eingeschränkt, dass sie nicht direkt auf die Hardware zugreifen können und sich auch nicht aus ihrer Privilegierungsebene befreien können. Der Zugriff auf den Speicherbereich anderer Prozesse wird meist durch Speichervirtualisierung verhindert. Somit wird gewährleistet, dass Programmcode in niedrigeren Privilegienstufen nicht eigenmächtig auf Programmcode oder Daten des Kernels und anderer Systemdienste in höheren Privilegienstufen zugreifen kann. Die gleiche Speichervirtualisierung wird üblicherweise auch eingesetzt, um unterschiedliche Prozesse voneinander zu isolieren. Da die unprivilegierten Prozesse auf Hardware nicht direkt zugreifen können, existieren sogenannte „Gates“, mit denen Programmcode aus höheren Stufen Programmcode aus niedrigeren Stufen aufrufen kann, insbesondere ist so die Programmierschnittstelle des Kernels erreichbar, um die notwendigen Aktionen anzufordern.

In der CPU und ggf. MMU müssen Schaltungen bestehen, die bei jedem Befehl bzw. Speicherzugriff prüfen, ob dieser in der aktuellen Privilegienstufe erlaubt ist. Falls ein Prozess etwas nicht Erlaubtes durchführen möchte, wird er unterbrochen und eine Betriebssystem-Routine aufgerufen, deren Aufgabe es ist, entsprechend zu reagieren.

Die meisten Prozessor-Architekturen bieten mindestens zwei Privilegienstufen: Programmcode in der höchstprivilegiertesten Stufe befindet sich im Kernelmodus (engl. kernel mode), Kernelraum (engl. kernel space) oder ist Superuser-Code (engl. super user code) – alle anderen im Benutzermodus (engl. user mode) oder Benutzerraum (engl. user space).

Ablauf eines Betriebssystemaufrufs

Bearbeiten

Ein (mit niedriger Berechtigung laufender) Prozess wählt durch geeignetes Setzen von CPU-Registern und Speicherbereichen die auszuführende Funktion des Betriebssystems und setzt die benötigten Parameter. Anschließend löst er per CPU-Befehl einen Softwareinterrupt aus. Der Prozess wird dadurch unterbrochen, die CPU wechselt in eine höhere Privilegienstufe („Kernel mode“) und setzt die Ausführung mit einer speziellen Betriebssystem-Routine fort. Diese sichert zunächst weitere (CPU-)Zustände, die nicht von der CPU selbst im Rahmen des Softwareinterrupts bereits gesichert wurden, um beispielsweise freie Register für das eigene Ablaufen zu haben. Anschließend agiert sie gemäß dem angeforderten Auftrag: Sie übergibt ihn an den zuständigen Treiber, reiht ihn in eine Warteschlange zur Abarbeitung durch einen Kernel-Thread ein oder kann evtl. den Auftrag selbst ausführen. In den ersten beiden Fällen wird anschließend meist ein vollständiger Kontextwechsel zu einem anderen Prozess durchgeführt, da der aufrufende Prozess erst weiter laufen kann, wenn das Betriebssystem den Auftrag vollständig abgearbeitet hat. Sobald der Auftrag vollständig abgearbeitet wurde, legt die Betriebssystem-Routine die Rückgabewerte in die Speicherbereiche des Prozesses ab und vermerkt ggf. weitere Rückgabewerte für bestimmte CPU-Register in dessen Prozesskontext. Der anfordernde Prozess wird als „bereit“ markiert und später im Rahmen des regulären Schedulings wieder (mittels Kontextwechsel) fortgesetzt: Eine spezielle CPU-Instruktion schließt den Kontextwechsel ab, die CPU „kehrt aus dem Softwareinterrupt zurück“, wodurch die CPU wieder in den User-Mode (niedrigste Privilegienstufe) zurück wechselt und die Ausführung des niedrig-berechtigten Prozesses direkt nach der Unterbrechungsstelle fortsetzt.

Einzelnachweise

Bearbeiten
  1. a b Presentation Device Driver Reference for OS/2 (Memento des Originals vom 16. Juni 2013 im Internet Archive)  Info: Der Archivlink wurde automatisch eingesetzt und noch nicht geprüft. Bitte prüfe Original- und Archivlink gemäß Anleitung und entferne dann diesen Hinweis.@1@2Vorlage:Webachiv/IABot/www.warpspeed.com.au auf warpspeed.com (englisch)
  2. Learn the architecture - AArch64 Exception Model: Privilege and Exception levels. In: ARM Developer Documentation. Arm Limited, 2022, abgerufen am 15. Dezember 2023 (englisch).
Bearbeiten