Ermöglicht es ein Programm im Benutzermodus nicht, auf den Kernelspeicher zuzugreifen und die Anweisungen IN und OUT auszuführen, um den Zweck des CPU-Modus zu umgehen?

19

Wenn sich die CPU im Benutzermodus befindet, kann die CPU keine privilegierten Anweisungen ausführen und kann nicht auf den Kernelspeicher zugreifen.

Wenn sich die CPU im Kernel-Modus befindet, kann sie alle Anweisungen ausführen und auf den gesamten Speicher zugreifen.

Jetzt in Linux kann ein Benutzer - Modus - Programm den gesamten Speicher zugreifen (mit /dev/mem) und kann die beiden privilegierten Befehle ausführen INund OUT(unter Verwendung iopl()glaube ich).

Ein Programm im Benutzermodus unter Linux kann also die meisten Dinge (ich denke die meisten Dinge), die im Kernel-Modus ausgeführt werden können.

Wenn Sie einem Programm im Benutzermodus nicht erlauben, über all diese Möglichkeiten zu verfügen, wird dies dem Zweck der Verwendung von CPU-Modi entgegengewirkt?

user341099
quelle

Antworten:

23

Ein Programm im Benutzermodus unter Linux kann also die meisten Dinge (ich denke die meisten Dinge), die im Kernel-Modus ausgeführt werden können.

Nun, nicht alle Programme im Benutzermodus können, nur diejenigen mit den entsprechenden Berechtigungen. Und das bestimmt der Kernel.

/dev/memist durch die üblichen Dateisystemzugriffsrechte und die CAP_SYS_RAWIOFähigkeit geschützt . iopl()und ioperm()sind auch durch die gleiche Fähigkeit eingeschränkt.

/dev/memkann auch komplett aus dem Kernel kompiliert werden ( CONFIG_DEVMEM).

Wenn Sie einem Programm im Benutzermodus nicht erlauben, über all diese Möglichkeiten zu verfügen, wird dies dem Zweck der Verwendung von CPU-Modi entgegengewirkt?

Vielleicht. Dies hängt davon ab, was privilegierte User-Space-Prozesse können sollen. User-Space-Prozesse können auch die gesamte Festplatte in den Papierkorb werfen, wenn sie Zugriff auf /dev/sda(oder ein gleichwertiges) Medium haben, obwohl dies den Zweck des Dateisystemtreibers für den Speicherzugriff außer Kraft setzt.

(Dann gibt es noch die Tatsache, dass iopl()die CPU-Privilegierungsmodi auf i386 verwendet werden, so dass nicht gesagt werden kann, dass sie ihren Zweck zunichte machen.)

ilkkachu
quelle
2
Sogar iopllässt nicht alle privilegierten Anweisungen zu, daher ist es immer noch nützlich, um sicherzustellen, dass ein fehlerhaftes User-Space-Programm nicht versehentlich ausgeführt invdwird, indem es durch einen beschädigten Funktionszeiger springt, der auf ausführbaren Speicher zeigt, der mit 0F 08Bytes beginnt . Ich habe eine Antwort mit einigen nicht sicherheitsrelevanten Gründen hinzugefügt, warum es nützlich ist, User-Space-Prozesse ihre Berechtigungen erhöhen zu lassen.
Peter Cordes
16

Nur auf die gleiche Weise, die die modprobeSicherheit "besiegt", indem neuer Code in den Kernel geladen wird.

Aus verschiedenen Gründen ist es manchmal sinnvoller, semiprivilegierten Code (wie Grafiktreiber auf dem X-Server) im User-Space auszuführen, als in einem Kernel-Thread.

  • In der Lage sein, killes leichter zu machen, es sei denn, es sperrt die HW.
  • Haben es Demand-Page seinen Code / Daten aus Dateien im Dateisystem. (Kernel-Speicher ist nicht auslagerbar)
  • Geben sie ihren eigenen virtuellen Adressraum , wo Fehler in der X - Server kann nur den X - Server zum Absturz bringen, ohne den Kernel , die hinunter.

Es trägt nicht viel zur Sicherheit bei, bietet jedoch große Vorteile in Bezug auf Zuverlässigkeit und Softwarearchitektur.

Das Einspielen von Grafiktreibern in den Kernel kann die Anzahl der Kontextwechsel zwischen X-Clients und X-Servern verringern, z. B. wenn nur ein Benutzer-> Kernel-> Benutzer Daten in einen anderen Use-Space-Prozess einspeisen muss, aber X-Server sind in der Vergangenheit zu groß und zu fehlerhaft um sie vollständig im Kernel zu haben.


Ja, böswilliger Code mit diesen Rechten könnte den Kernel übernehmen, wenn er dies wünscht, und dazu verwenden /dev/mem, den Kernelcode zu ändern.

Oder führen Sie beispielsweise auf x86 eine cliAnweisung aus, um Interrupts auf diesem Core zu deaktivieren, nachdem Sie einen ioplSystemaufruf ausgeführt haben, um die E / A-Berechtigungsstufe auf 0 zu setzen.

Aber auch x86 ioplbietet "nur" Zugriff auf einige Anweisungen : in / out (und die String-Versionen ins / outs) und cli / sti. Es lässt nicht Sie verwenden rdmsroder wrmsrzu lesen oder zu schreiben „modellspezifische Register“ (zB , IA32_LSTARdie den Kernel Eintrittspunktadresse für die x86-64 setzt syscallAnweisung) oder Verwendung lidtder Interrupt-Deskriptortabelle ersetzen (die Sie völlig würden nehmen lassen über die Maschine aus dem vorhandenen Kernel, zumindest auf diesem Kern.)

Sie können nicht einmal Steuerregister lesen (wie CR3, das die physikalische Adresse des Seitenverzeichnisses der obersten Ebene enthält, das ein angreifender Prozess möglicherweise als Versatz /dev/memfür die Änderung seiner eigenen Seitentabellen als Alternative zu einer mmapErweiterung von verwendet /dev/mem. )

invd(Ungültigmachen aller Caches ohne Zurückschreiben !! ( Anwendungsfall = frühes BIOS, bevor RAM konfiguriert wird)) ist ein weiterer Spaß, der immer die volle CPL 0 (aktuelle Berechtigungsstufe) erfordert, nicht nur IOPL. Even wbinvdist privilegiert, weil es so langsam (und nicht unterbrechbar) ist und alle Caches über alle Kerne hinweg leeren muss. (Siehe Gibt es eine Möglichkeit , die gesamte CPU - Cache zu einem Programm mit Bezug zu spülen? Und WBINVD Anweisung Nutzung )

Fehler, die zu einem Sprung zu einer schlechten Adresse führen, bei der Daten als Code ausgeführt werden, können daher keine dieser Anweisungen versehentlich auf einem User-Space-X-Server ausführen.


Die aktuelle Berechtigungsstufe (im geschützten und langen Modus) sind die niedrigen 2 Bits von cs(dem Codesegment-Selektor) . mov eax, cs/ and eax, 3arbeitet in einem beliebigen Modus, um die Berechtigungsstufe zu lesen.

Um die Berechtigungsstufe zu schreiben, müssen Sie ein jmp faroder call farfestlegen CS:RIP(der GDT / LDT-Eintrag für das Zielsegment kann sie jedoch basierend auf der alten Berechtigungsstufe einschränken, weshalb der Benutzer-Space dies nicht tun kann, um sich selbst zu erhöhen). Oder Sie verwenden intoder syscall, um an einem Kernel-Einstiegspunkt zu Ring 0 zu wechseln.

Peter Cordes
quelle
Eigentlich bin ich mir ziemlich sicher, dass es nur ein Code "Selector" in Intel Parlace ist. Es war ein Segment in der 8086/8088, möglicherweise in der 80186, aber in der 80286 wurde es als Selektor bezeichnet, und ich glaube nicht, dass sie diese Terminologie seitdem offiziell geändert haben.
ein Lebenslauf