Autor Thema: Priviledge Level bei CPU Anweisesungen angeben  (Gelesen 6162 mal)

tpo

  • Beiträge: 2
    • Profil anzeigen
Gespeichert
« am: 29. August 2011, 09:46 »
Hallo Lowleveler/Wikianer,

war http://www.lowlevel.eu/wiki/LDT am lesen. Da steht, dass man den Befehl "lldt" ausführen kann um eine LDT zu laden. Nur steht da nicht, wann man den Befehl benutzen darf. Wäre es nicht empfehlenswert allgemein bei solchen und ähnlichen Artikeln jeweils anzugeben, dass die Befehle nur im Ring 0 ausgeführt werden können?

Der Kontext aus dem ich das schreibe sind meine Recherchen zu Speicherschutz für Threads - LDTs würden sich als geeigneter Mechanismus anbieten ... wenn sie denn aus dem Userspace heraus manipulierbar wären. Sind sie aber nicht...

Die Tatsache, dass Artikel - nicht nur auf Lowlevel - zu lowlevel Themen oft "vergessen" anzugeben, ob die jeweils diskutierte Aktion nur im Ring 0 ausgeführt werden kann oder nicht, scheint verbreitet. Diese Art von Auslassung scheint mir zu vernachlässigen, dass sich das Betriebssystem ja z.T. auch im Ring 3 "abspielt". Viel "OS" Funktionalität läuft heute ja im Userspace z.B. in Bibliotheken und System Diensten/Daemons ab. Und eben dort ist es auch interessant auf "Low Level" Funktionalität zugreifen zu können.

Mein Vorschlag wäre also, wenn es nicht total offensichtlich ist, bei Artikeln anzugeben, ob die Funktionalität exklusiv nur für Ring 0 ist.
*t

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 29. August 2011, 11:37 »
Keine Ahnung, mir kommt das selbstverständlich vor, dass das nur in Ring 0 geht. Wenn es in Ring 3 ginge, könntest du den ganzen Speicherschutz vergessen, weil der Userspace sich selber definieren dürfte, was er benutzen darf und was nicht...

Es empfiehlt sich sowieso immer, für die Details auch mal in die Handbücher der Prozessorhersteller zu schauen. Dort steht dann ganz genau, was eine Instruktion macht und in welchen Fällen sie wie schiefgeht.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 29. August 2011, 14:02 »
Hallo,


Wenn es in Ring 3 ginge, könntest du den ganzen Speicherschutz vergessen, weil der Userspace sich selber definieren dürfte, was er benutzen darf und was nicht...
Da der LLDT nur einen Selector für die GDT lädt und nicht selber eine beliebige (selbst erstellte) LDT laden kann ist das eventuell nicht ganz so drastisch, trotzdem besteht natürlich das Risiko das ein Prozess so die LDT eines anderen Prozesses laden könnte und damit wäre der Speicherschutz (zwischen den Prozessen) auf jeden Fall komplett ausgehebelt.

Rein vom Bauchgefühl her würde ich sagen LLDT ist Ring 0 only, alles andere macht auch keinen Sinn.
Da hier meines Wissens nach noch niemand den Befehl LLDT benutzt hat dürfte diese Lücke im Wiki auch verständlich sein. Bei solchen exotischen Spezialdingen dürfte nur ein Blick ins Manual 100% Gewissheit geben, nebst dessen das bei so unüblichen Dingen das Wiki auch keinen Anspruch auf Vollständigkeit erheben muss.


Der Kontext aus dem ich das schreibe sind meine Recherchen zu Speicherschutz für Threads - LDTs würden sich als geeigneter Mechanismus anbieten
Du möchtest die erreichbaren Adressräume von Threads aus einem Prozess individuell einschränken können? Dass das dem üblichen Programmiermodel für Threads widerspricht ist Dir bewusst?
Die LDT wirkt nur auf Segmente (das drunter liegende Paging wird davon nicht beeinflusst), wenn man damit Speicherschutz machen will dann geht das nur wenn die Programme auch in einem segmentiertem Speichermodel arbeiten.
Das es im x86-Long-Mode (64Bit-Mode) keine LDT mehr gibt (und auf allen anderen CPUs auch nie gab) ist sicher auch bewusst, oder?

Trotzdem würde mich dazu ein klein wenig mehr interessieren. Was genau hast Du denn vor?
Eventuell gibt es für Dein Vorhaben noch andere Wege ohne die LDT.

... wenn sie denn aus dem Userspace heraus manipulierbar wären. Sind sie aber nicht...
Wenn die LDT aus dem User-Mode heraus änderbar/wechselbar wäre würde sie grundsätzlich keinen wirksamen Speicherschutz darstellen. Speicherschutz ist immer nur dann wirksam wenn seine Mechanismen ausschließlich nur vom Kernel manipulierbar sind.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 29. August 2011, 15:36 »
Wenn die LDT aus dem User-Mode heraus änderbar/wechselbar wäre würde sie grundsätzlich keinen wirksamen Speicherschutz darstellen. Speicherschutz ist immer nur dann wirksam wenn seine Mechanismen ausschließlich nur vom Kernel manipulierbar sind.
Von einer höheren Privilegstufe. Wenn schon exotische Features, dann haben wir auf i386 auch mindestens vier davon. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

tpo

  • Beiträge: 2
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 30. August 2011, 15:51 »

Wenn es in Ring 3 ginge, könntest du den ganzen Speicherschutz vergessen, weil der Userspace sich selber definieren dürfte, was er benutzen darf und was nicht...

Da der LLDT nur einen Selector für die GDT lädt und nicht selber eine beliebige (selbst erstellte) LDT laden kann ist das eventuell nicht ganz so drastisch, trotzdem besteht natürlich das Risiko das ein Prozess so die LDT eines anderen Prozesses laden könnte und damit wäre der Speicherschutz (zwischen den Prozessen) auf jeden Fall komplett ausgehebelt.

Wahrscheinlich schon. Wobei, es hat da noch die "priviledge bits" welche man vielleicht irgendwie nutzen könnte und es hat auch 4 Ringe, von denen nur zwei gebraucht sind, es ist also konzeptuel vorstellbar Userspace, sagen wir Mal, im Ring 1 laufen zu lassen und dann noch 2 weitere Ringe im Userspace zur Verfügung zu haben.


Der Kontext aus dem ich das schreibe sind meine Recherchen zu Speicherschutz für Threads - LDTs würden sich als geeigneter Mechanismus anbieten

Du möchtest die erreichbaren Adressräume von Threads aus einem Prozess individuell einschränken können? Dass das dem üblichen Programmiermodel für Threads widerspricht ist Dir bewusst?

Jein. Das Thread-Programmiermodel hat enthält diverse Elemente, einige davon möchte ich beibehalten:

  • das API
  • kein zwingendes Kontextswitching
  • keine eigene PID
  • miteinander geteilte Kernelseitige Ressourcen (Filedeskriptoren, Semaphoren, Shared Memory, Permissions etc.)

andere sind stärker problembehaftet, wie eben:

  • kein Speicherschutz

letzteres führt zu diversen Problemen:

  • Programmierfehler sind schwerwiegender, da sie nicht nur auf den fehlerhaften Thread beschränkt sind, sondern potentiell auch andere "unschuldige" Threads runterreissen können
  • von Sicherheitsproblemen sind alle Threads betroffen

Dass es im x86-Long-Mode (64Bit-Mode) keine LDT mehr gibt (und auf allen anderen CPUs auch nie gab) ist sicher auch bewusst, oder?

Ah nein, das wusste ich nicht.

Trotzdem würde mich dazu ein klein wenig mehr interessieren. Was genau hast Du denn vor?
Eventuell gibt es für Dein Vorhaben noch andere Wege ohne die LDT.

Siehe oben. Eventuell kann ich mit Linux' "clone" Befehl erreichen, was ich möchte, der hat sehr feingranularige Optionen. Ausserdem können viele der PThread Funktionen auch Cross-Prozess verwendet werden. Mal schauen, wie weit ich da komme.

(Danke für die Antworten btw!)
*t

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 30. August 2011, 20:30 »
Hallo,


Wobei, es hat da noch die "priviledge bits" welche man vielleicht irgendwie nutzen könnte
Hä, was meinst Du?

und es hat auch 4 Ringe, von denen nur zwei gebraucht sind, es ist also konzeptuel vorstellbar Userspace, sagen wir Mal, im Ring 1 laufen zu lassen und dann noch 2 weitere Ringe im Userspace zur Verfügung zu haben.
Sobald aber die Schutzmechanismen sich aus einer niedrigeren Privilegierungsstufe manipulieren lassen sind die höheren Stufen nicht mehr sicher, es macht also IMHO keinen Sinn diese Schutzmechanismen einer anderen Stufe außer der höchsten anzuvertrauen. Oder anders ausgedrückt: es lohnt sich nicht eine höhere Priviligierungsstufe zu haben außer der in welcher die Speicherschutzmechanismen manipulierbar sind.

Jein. Das Thread-Programmiermodel hat enthält diverse Elemente, einige davon möchte ich beibehalten:
  • das API
  • kein zwingendes Kontextswitching
  • keine eigene PID
  • miteinander geteilte Kernelseitige Ressourcen (Filedeskriptoren, Semaphoren, Shared Memory, Permissions etc.)
Der fehlende Kontextswitch bedeutet aber ein gemeinsames Paging-Directory (oder eine LDT für jeweils einen ganzen Prozess) und damit eben auch einen gemeinsamen virtuellen Adressraum und damit eben kein Speicherschutz zwischen den Threads.

andere sind stärker problembehaftet, wie eben:
  • kein Speicherschutz
Das ist beim Thread-Model aber auch gerade so gewollt. Jeder Thread soll an alle Speicherbereiche des gesamten Prozesses ran kommen. Man kann sogar von einer Stackvariablen des einen Threads einen Pointer bilden und diesen einem anderen Thread geben so das der andere Thread lokale Variablen der ersten Threads manipulieren kann. Das ist im Model des Threading explizit so gewollt, wenn das nicht mehr funktioniert dann dürften wohl viele existierende Multithreading-Programme nicht mehr funktionieren.

Die Nachteile die Du ableitest sind meiner persönlichen Meinung nach keine echten Nachteile, schwerwiegender sind da IMHO Programmierfehler die durch die Nebenläufigkeit ansich entstehen (sowas wie Dead-Locks u.ä.). Ich bin nicht der Meinung das ein Programm eine größere Angriffsfläche bietet nur weil verschiedene Code-Teile dieses Programms gleichzeitig laufen können. Klar ist es ärgerlich das ein Programm das z.B. viele Workerthreads gestartet hat komplett gekillt wird (und so auch die Arbeit intakter Workerthreads verloren ist) nur weil ein einzelner Workerthread eine Exception verursacht hat, aber dagegen kann man geziehlt vorgehen indem das entsprechende Signal im Programm angemessen behandelt wird (die Behandlung kann durchaus auch beinhalten das die anderen Workerthreads erst fertig arbeiten und ihre Ergebnisse abspeichern dürfen bevor der Prozess gekillt wird).

Wimre ist clone sowas ähnliches wie fork aber mit ein paar zusätzlichen Optionen, trotzdem kann man mit clone wimre keine echten Threads erstellen.

Ich habe aber immer noch nicht so genau verstanden was Du eigentlich erreichen willst bzw. für was für ein Problem Du eine Lösung entwickeln möchtest.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 30. August 2011, 21:33 »
Threads zeichnen sich dadurch aus, dass sie in einem gemeinsamen Kontext laufen. Du ersparst dir also Kontextswitches und bezahlst mit dem fehlenden Speicherschutz. Wenn du Speicherschutz für Threads ("leichtgewichtige Prozesse") implementieren willst, dann musst du auch einen "leichtgewichtigen Kontextswitch" implementieren.

Ob der hinreichend billig ist, um sicher zu sein, kann ich nicht einschätzen. Vermute aber mal, dass du dann statt Threads gleich Prozesse nutzen solltest, wenn der fehlende Speicherschutz für dich ein Problem darstellt.

Gruß,
Svenska

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 30. August 2011, 22:11 »
Wimre ist clone sowas ähnliches wie fork aber mit ein paar zusätzlichen Optionen, trotzdem kann man mit clone wimre keine echten Threads erstellen.
Was verwendest du sonst, um Threads zu erstellen? Hat Linux denn noch mehr Funktionen dafür?

Die Manpage sagt jedenfalls folgendes dazu:
Zitat
Unlike fork(2), these calls allow the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors,  and  the  table  of  signal  handlers.
[...]
The main use of clone() is to implement threads: multiple threads of control in a program that run concurrently in a shared memory space.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 31. August 2011, 11:40 »
Hallo,


Was verwendest du sonst, um Threads zu erstellen? Hat Linux denn noch mehr Funktionen dafür?
Soweit ich weiß wurde clone früher für die ersten (etwas vermurksten) Threadimplementierungen unter Linux benutzt (die hatten deswegen auch unterschiedliche PIDs pro Thread usw.), ich bin eigentlich davon ausgegangen das es heute dafür eine native Kernel-API gibt um mit Threads umzugehen. Aber wenn stattdessen clone etwas aufgebohrt wurde dann kann ich damit auch gut leben.


@tpo:
Vielleicht ist clone ja die Lösung Deiner Probleme, offensichtlich kann man damit recht fein konfigurieren was geteilt werden soll und was nicht, aber beurteilen kann ich das nicht da ich Dein Problem nicht kenne.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 31. August 2011, 13:56 »
Laut Manpage ist clone() linux-spezifisch und nicht portabel. Die POSIX-Funktion, um einen Thread zu erzeugen ist pthread_create(). Intern nutzen diese Funktionen aber den clone-Syscall.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 02. September 2011, 22:00 »
Hallo,


.... Intern nutzen diese Funktionen aber den clone-Syscall.
Der clone-Syscall scheint demnach tatsächlich fein genug bestimmen zu können was alles geshared wird und was nicht. Da steckt sicher ein interessantes Stück POSIX-(In)Kompatibilitäts-Geschichte dahinter. ;)


Zu beachten ist aber auch das wenn der virtuelle Adressraum nicht zu 100% und ungeschützt geteilt wird dass das dann bedeutet das ein neues Paging-Directory erstellt werden muss und damit gibt es dann keinen "leichtgewichtigen Kontextswitch" mehr, egal ob man das dann Thread/Prozess/Task/sonstwie/.... nennt.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 02. September 2011, 22:06 »
Wie Threads real implementiert werden, ist doch total egal. Solange pthread_* angeboten werden, ist das Threading POSIX-kompatibel. Linux nutzt dafür die clone()-Funktion der libc, die wiederum nutzt den privaten clone-Syscall, andere Betriebssysteme entsprechend andere Dinge...

 

Einloggen