Autor Thema: erik´s IPC Idee ;)  (Gelesen 25358 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #40 am: 28. January 2011, 09:55 »
Ich habe mir überlegt um dem etwas vorzubeugen könnte ich dem Popup-Thread meine fixed-Size Nachricht und die Größe des zusätzlichen Speichers mitgeben und der Code kann dann entscheiden ob die Größe für diesen Nachrichtentyp in Ordnung geht (was ja auch relativ ist).

Nur kostet das halt wieder Performance (es muss, wenn noch Speicher mitgeschickt wurde, noch ein Syscall gemacht werden um diesen auch mappen zu lassen), aber ich habe die Möglichkeit zu sagen, das diese Nachricht nicht weiter bearbeitet werden soll.

Was ich noch nicht weiß wie ich es löse ist, was passiert wenn der virtuelle Speicher des Services zu voll ist um eine Nachricht (also den zusätzlichen Speicher) empfangen zu können. Sollte ich dann warten bis genug Speicher frei ist (wie sollte man das lösen) oder sollte ich einfach dem Clienten eine Nachricht schicken das der Service nicht genügend Speicher hat um die Nachricht zu empfangen?

Rein theoretisch wäre es möglich das ein Client 256MB mit sinnlosen Daten hat und 12 Threads und alle Threads machen eine RPC Anfrage an den Storage-Server mit den 256MB. Dann ist der Storage-Server erstmal beschäftigt und kann keine weiteren Anfragen mehr annehmen, weil er keinen freien virtuellen Speicher mehr hat. Das ist zwar nur kurzzeitig, aber es ist halt möglich.

Ich weiß auch nicht wieviel Sinn es macht ein gewisses Limit einzuführen (z.B. 32MB). Eigentlich sollte es nicht so schlimm sein, wenn ein Client mehrere Anfragen machen müsste um mehr Daten zu bekommen oder? Weil dann bräuchte es schon 96 Threads/Clients für eine DoS-Attacke (sind zwar auch nicht zu viele, aber immerhin ;) ).

Was die FIFO-Queue betrifft mache ich mir da mehr sorgen um das malloc() was ich da bräuchte (Thread-safe).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #41 am: 28. January 2011, 12:25 »
Hallo,


Ich habe mir überlegt um dem etwas vorzubeugen könnte ich dem Popup-Thread meine fixed-Size Nachricht und die Größe des zusätzlichen Speichers mitgeben und der Code kann dann entscheiden ob die Größe für diesen Nachrichtentyp in Ordnung geht (was ja auch relativ ist).
Das köntest Du eventuell auch nur für große Nachrichten (> 4 MByte) machen. Der PopUp-Thread bekommt ja immer einen Pointer und eine Größenangabe und wenn der Pointer NULL ist obwohl die Größenangabe größer 0 ist (wenn die Größenangabe gleich 0 ist dann muss auch immer der Pointer NULL sein weil dann gar keine Zusatzdaten mitkommen, das ist auch ein gültiger Zustand, ungültig ist nur wenn Pointer != NULL aber Größenangabe == 0 ist) dann muss der PopUp-Thread das Mappen explizit anfordern wenn er es denn für angebracht hält. Das Problem ist IMHO das der Client seine Anfragen immer so formulieren kann das der Service sie für angebracht hält, ein fwrite(file, pointer, 256MByte) ist grundsätzlich erstmal immer eine gültige Aktion. Ich denke das Problem mit Deinem einem virtuellen Adressraum wirst Du so oder so haben, außer Du begrenzt die maximale RPC-Datenmenge grundsätzlich im Kernel immer auf einen festen Wert von z.B. 16 MByte (wenn diese Grenze groß genug ist stellt die auch kein Performance-Problem dar) aber dann braucht man für den DoS-Angriff einfach nur mehr Threads. Bei meinem Konzept hab ich vorgesehen das der Service beim Einrichten eines Ports immer auch Maximal-Größen angeben muss, genauso wie die maximale Anzahl an PopUp-Threads, z.B. der UDP-Service könnte da 64 kBytes angeben weil UDP eh nicht mehr Daten pro Paket aufnimmt.
Auf der anderen Seite muss man auch ehrlich sagen das wenn ein Schadprogramm einmal richtig läuft dann ist es eh schon fast zu spät. Die eigentliche Verteidigungslinie (Browser oder auch die Ausführungsverhinderung von nicht vertrauenswürdigen Programmen) muss schon davor ansetzen. Ich denke wenn wir zu sehr versuchen möglichst viel Sicherheit in IPC zu bauen dann arbeiten wir da wohl an der falschen Stelle (was nicht heißen soll dass das Thema Sicherheit bei IPC ignoriert werden soll), es kommt IMHO auf einen guten Kompromiss an.

Was ich noch nicht weiß wie ich es löse ist, was passiert wenn der virtuelle Speicher des Services zu voll ist um eine Nachricht (also den zusätzlichen Speicher) empfangen zu können. Sollte ich dann warten bis genug Speicher frei ist (wie sollte man das lösen) oder sollte ich einfach dem Clienten eine Nachricht schicken das der Service nicht genügend Speicher hat um die Nachricht zu empfangen?
Bei asynchronen Messages/Signalen würde ich von vornherein eine sehr konservative maximale Datengröße vorgeben, ich denke 64 kBytes sollten für alle möglichen Anwendungsfälle mehr als ausreichend sein (zumindest für ne Mausposition/Fenstergröße bei GUI-Nachrichten oder ein Exception-Descriptor bei Page-Fault-Signalen o.ä. sollte das immer bequem reichen).
Bei synchronen RPC-Dingen blockiert doch der RPC-send+recv-Syscall den Client eh bis alles fertig ist und bei mir liefert der auch 2 getrennte Rückgabewerte zurück, einmal vom Kernel der über den Erfolg/Misserfolg (bei Misserfolg mit nem richtigen Error-Code) des eigentlichen RPC-Vorgangs informiert und einen anderen der vom Service kommt und über den Status der eigentlichen Service-Routine Auskunft gibt. Wenn der erste Rückgabewert (vom Kernel) schon angibt das der RPC-Vorgang fehl schlug (egal ob nun kein virtueller Speicher mehr beim Service vorhanden war oder der Service nur Speicherbereiche bis x MByte akzeptiert aber der Client mehr übergeben wollte oder was anderes) dann braucht der Client die eigentliche Antwort nicht weiter analysieren sondern kann entscheiden ob er es noch mal probiert oder ob er auf gibt und dem User ne Fehlermeldung präsentiert.

Was die FIFO-Queue betrifft mache ich mir da mehr sorgen um das malloc() was ich da bräuchte (Thread-safe).
Du benötigst doch eh eine multithreading-taugliche libc, dann nimm die doch auch für Singlethreaded-Programme. Ein einfaches Lock wie das http://forum.lowlevel.eu/index.php?topic=2468.msg27659#msg27659 sollte für Dinge wie malloc völlig reichen da malloc ja üblicherweise schneller fertig ist als ein einfacher Kontext-Switch dauert so das es eher kontraproduktiv wäre die Zeitscheibe frei zu geben (aber darüber haben wir ja in dem zitierten Thread bereits ausführlich gestritten).


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

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #42 am: 28. January 2011, 12:35 »
Zitat von: erik
Das köntest Du eventuell auch nur für große Nachrichten (> 4 MByte) machen. Der PopUp-Thread bekommt ja immer einen Pointer und eine Größenangabe und wenn der Pointer NULL ist obwohl die Größenangabe größer 0 ist (wenn die Größenangabe gleich 0 ist dann muss auch immer der Pointer NULL sein weil dann gar keine Zusatzdaten mitkommen, das ist auch ein gültiger Zustand, ungültig ist nur wenn Pointer != NULL aber Größenangabe == 0 ist) dann muss der PopUp-Thread das Mappen explizit anfordern wenn er es denn für angebracht hält.
Die Idee gefällt mir, vllt könnte man das sogar dynamisch machen, sprich bei der Port-Erstellung entscheidet das der Service (vorteilhaft z.B. für dein UDP-Bsp.) ab welcher Größe der Speicher explizit gemappt werden muss und was die maximale Größe überhaupt ist.

Wenn ich meine libc schreibe, will ich eigentlich versuchen lock-free Algos zu verwenden. Denn die erreichen ja einen höheren Durchsatz bei mehreren Threads und das kann vollständig im UserSpace laufen und die Performance auf ein-CPU Systemen sollte nicht viel schlechter sein als ganz ohne Lock.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #43 am: 28. January 2011, 20:43 »
Wenn ich irgendwann einmal einen besseren Scheduler habe der Gruppen unterstützt dann sollen die PopUp-Threads immer den ursprünglichen Client-Prozess logisch zugeordnet werden so das sie z.B. der PopUp-Thread im SATA-Treiber seine CPU-Zeit immer noch von dem Prozess holen muss der ursprünglich das fread an den VFS-Service geschickt hat.
Das kann böse nach hinten losgehen, wenn ein niedrigpriorisierter Prozess deinen SATA-Treiber (der ja begrenzt seriell arbeiten muss) blockieren kann, während gleichzeitig höherpriorisierte Prozesse dafür sorgen, dass CPU-Zeit knapp ist.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #44 am: 28. January 2011, 21:44 »
Hallo,


Das kann böse nach hinten losgehen, wenn ein niedrigpriorisierter Prozess deinen SATA-Treiber (der ja begrenzt seriell arbeiten muss) blockieren kann, während gleichzeitig höherpriorisierte Prozesse dafür sorgen, dass CPU-Zeit knapp ist.
Ja, das ist mir auch schon aufgefallen. Ich möchte ja in meine CPUs die Fähigkeit einbauen das der User-Mode-Code für kurze/begrenzte Zeit die Annahme von IRQs und anderen Unterbrechungen unterbinden kann (was ja eigentlich gar nicht erlaubt ist), das würde es ermöglichen dafür zu sorgen das der Code wenigstens nicht während einer Critical-Section unterbrochen wird. Da die Datenstrukturen in einem SATA-AHCI-Treiber noch recht harmlos sind (es geht ja nur um das Management von Jobs) sollte das dort klappen aber bei einem richtigen Dateisystem-Treiber ist das schon wieder was anderes, da könnten die kritischen Code-Abschnitte durchaus länger sein. Das Problem entsteht eben dann wenn ein Thread mit niedriger Priorität unterbrochen wird während er einen Lock hält und dann eventuell für sehr lange Zeit nicht mehr dran kommt um diesen Lock wieder frei zu geben. Hier wäre es besser wenn der PopUp-Thread immer eine für den Service ausreichend hohe Priorität hat (eventuell gibt man allen Threads in einem Service die selbe Priorität und der Service muss das Aufteilen von Bandbreite o.ä. selber managen) aber die CPU-Zeit trotzdem dem Prozess geklaut wird (dann würden die restlichen Threads in einem Prozess eventuell verhungern während einer davon einen Service in Anspruch nimmt).

Hm, alles irgendwie doof, die optimale Lösung wird mir da ganz gewiss nicht mehr Heute einfallen. Was ich auch nicht möchte ist das ich versuche jedes blöde SW-Design-Problem wieder mit HW zu kompensieren, das geht nur bis zu einem gewissen Grad. Das vorübergehende Abschalten der Unterbrechbarkeit ist zwar eine tolle Idee (und soll auch umgesetzt werden) aber das als Ausrede zu benutzen um nen blöden Scheduler zu programmieren ist wieder nicht so klug.

Werden den bei den aktuellen OSen eigentlich die I/O-Anfragen u.ä. mit der Priorität des Initiator-Prozesses gewichtet? (ich tippe mal auf nein oder nur sehr begrenzt)
Lohnt es sich eigentlich im IPC-System zu versuchen Anti-DoS-Techniken einzubauen oder sollte man lieber versuchen dafür zu sorgen das böse Prozesse erst gar nicht gestartet werden? (ich tippe mal auf letzteres)


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 #45 am: 28. January 2011, 22:20 »
Du möchtest die Priorität von Treibern während des Syscalls auf die Priorität des aufrufenden Prozesses herabstufen. Die übliche Lösung ist es, die Priorität der Anwendungen während des Syscalls auf die Priorität des Treibers hochzustufen. Damit verhinderst du Prioritätsinversion.

Jemand, der ein Lock hat, muss temporär höher priorisiert sein, als jemand, der kein Lock hat. Und das kannst du auch mit Hardware nicht umkehren.

Die Antwort lautet nein, aber der CFS unter Linux kann Control Groups auch für I/O machen, soweit ich weiß. Allerdings ist das auf einem Desktop-System, naja, nur begrenzt wichtig. (Wenn du aber eine VServer-Umgebung aufbaust, ist das wieder unglaublich praktisch.)

Ich glaube, dein bester Schutz gegen ein DoS des IPC ist es, das IPC so schnell (und skalierend) zu bauen, dass die Flaschenhälse woanders auflaufen. Und der allerbeste Schutz gegen einen DoS ist natürlich, wenn das System die Last einfach schluckt und nicht umfällt. ;-)

Notfalls hast du halt eine DoS-Heuristik, die die Priorität von übermäßig IPC-produzierenden herabsetzt, auch auf Kosten von Locks. (Wird dann allerdings böse, wenn jemand einen I²C oder SPI mit Bitbanging implementieren möchte...)

Gruß

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #46 am: 28. January 2011, 22:25 »
Zitat von: erik
Werden den bei den aktuellen OSen eigentlich die I/O-Anfragen u.ä. mit der Priorität des Initiator-Prozesses gewichtet? (ich tippe mal auf nein oder nur sehr begrenzt)
Irgendwer hatte es mal getestet das man einen kurzen "boost" (höhere Priorität) bekommt wenn man eine kritische Sektion "betritt". Soll aber nicht wirklich was gebracht haben (so das es spürbar war) und hängt auch sehr davon ab wie dein Scheduler funktioniert.

Eine Lösung um das Verhungern zu lösen sind lock-free Algos und lass dir gesagt sein, das ist ein schei*** Thema ;) (ich habe heute den ganzen Tag versucht ne Queue lock-free zu implementieren und ich habe es noch immer nicht geschafft, bin nun aber soweit das ich vllt meinen Kernel in C++ neu schreibe ;) )

Zitat von: erik
Lohnt es sich eigentlich im IPC-System zu versuchen Anti-DoS-Techniken einzubauen oder sollte man lieber versuchen dafür zu sorgen das böse Prozesse erst gar nicht gestartet werden? (ich tippe mal auf letzteres)
Wenn du dein IPC-System DoS sicher machen würdest, würdest du damit Symptombekämpfung betreiben. versuchst du das böse Prozesse erst gar nicht gestartet werden, wäre das eine Ursachenbekämpfung. Was wird wohl besser (aber nicht unbedingt einfacher) sein ;)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #47 am: 29. January 2011, 09:17 »
Hallo,


Du möchtest die Priorität von Treibern während des Syscalls auf die Priorität des aufrufenden Prozesses herabstufen.
Nein, ich dachte daran das die Priorität des einzelnen PopUp-Threads dem Client-Thread (der den RPC initiiert hat) entspricht aber das ist ja Mist wegen den Locks. Eine andere Idee wäre es das alle PopUp-Threads automatisch mit der Default-Priorität des Treiber laufen (also die Priorität des Clients ignoriert wird), das tät zumindest die wenigsten Probleme mit Locking, Scheduler usw. verursachen aber es verhindert auch das eben die Jobs eines Clients mit niedriger Priorität auch im Service nur mit niedriger Priorität behandelt werden (der würde alle Jobs gleich behandeln und damit den DoS-Angriffen die Tür weit öffnen).

Die übliche Lösung ist es, die Priorität der Anwendungen während des Syscalls auf die Priorität des Treibers hochzustufen. Damit verhinderst du Prioritätsinversion.
Das finde ich irgendwie doof (täte dann ja auch die restlichen Threads im Client-Prozess betreffen), und für mein RPC-System völlig unnötig. Die Priorität des Clients ist für den Service absolut ohne Belang, nur die Priorität des entsprechenden PopUp-Threads kann den Service beeinflussen.

Jemand, der ein Lock hat, muss temporär höher priorisiert sein, als jemand, der kein Lock hat. Und das kannst du auch mit Hardware nicht umkehren.
Ich will das auch nicht mit Hardware umkehren sondern vereinfachen. Wenn ein Stück User-Mode-Code für kurze Zeit (so lange er den Lock hält) nicht unterbrechbar ist hat er quasi die höchste Priorität (er wäre damit auf Kernel-Niveau). Ich muss nur sicherstellen das die CPU dafür sorgt dass das von keinem User-Mode-Code irgendwie bösartig ausgenutzt werden kann, also der Zeitraum muss definitiv eng begrenzt sein und es sollte von der Restzeitscheibe immer eine kleine Strafe abgezogen werden (der Zeitscheiben-Counter zählt in der kurzen Zeit nicht aber es wird am Beginn immer gleich X Mikrosekunden abgezogen).

Die Antwort lautet nein, ....
Dachte ich mir doch.

Ich glaube, dein bester Schutz gegen ein DoS des IPC ist es, das IPC so schnell (und skalierend) zu bauen, dass die Flaschenhälse woanders auflaufen. Und der allerbeste Schutz gegen einen DoS ist natürlich, wenn das System die Last einfach schluckt und nicht umfällt. ;-)
Sehe ich ähnlich. Wichtig ist das ein Angriff nicht zu Instabilitäten oder zu Lecks in der Rechteverwaltung bzw. Rechtedurchsetzung führt.

Notfalls hast du halt eine DoS-Heuristik, die die Priorität von übermäßig IPC-produzierenden herabsetzt, auch auf Kosten von Locks. (Wird dann allerdings böse, wenn jemand einen I²C oder SPI mit Bitbanging implementieren möchte...)
ls wäre auch so ein Prozess der quasi nur RPC macht. Ich denke mit solch einer Beschränkung muss man nur anfangen wenn man merkt dass das System überlastet ist. Wenn ich 4 CPUs hab und eine davon mit ls oder Bitbanging ausgelastet ist kann mir das doch völlig egal sein.


Wenn du dein IPC-System DoS sicher machen würdest, würdest du damit Symptombekämpfung betreiben. versuchst du das böse Prozesse erst gar nicht gestartet werden, wäre das eine Ursachenbekämpfung. Was wird wohl besser (aber nicht unbedingt einfacher) sein ;)
Dann sind wir uns ja einig.
Wir versuchen DoS-Angriffe auf RPC zwar zu verhindern bzw. abzumildern aber wenn wir uns zwischen einem anständigen Design und einer Krückenlösung die aber weniger DoS-empfindlich ist entscheiden müssen nehmen wir auf jeden Fall das anständige Design. Ich denke bei RPC geht die Schönwetter-Performance vor, den Regenschirm sollen andere Stellen halten.


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 #48 am: 31. January 2011, 09:26 »
(ich habe heute den ganzen Tag versucht ne Queue lock-free zu implementieren und ich habe es noch immer nicht geschafft, bin nun aber soweit das ich vllt meinen Kernel in C++ neu schreibe ;) )
Hm? Für eine einfach verkettete Queue ist das doch trivial?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #49 am: 31. January 2011, 11:56 »
Zitat von: taljeth
Hm? Für eine einfach verkettete Queue ist das doch trivial?
Na dann mal her damit, aber für C++ ohne das man nen GC benutzen muss ;)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #50 am: 31. January 2011, 16:45 »
Die übliche Lösung ist es, die Priorität der Anwendungen während des Syscalls auf die Priorität des Treibers hochzustufen. Damit verhinderst du Prioritätsinversion.
Das finde ich irgendwie doof (täte dann ja auch die restlichen Threads im Client-Prozess betreffen), und für mein RPC-System völlig unnötig. Die Priorität des Clients ist für den Service absolut ohne Belang, nur die Priorität des entsprechenden PopUp-Threads kann den Service beeinflussen.
Mein Fehler, ich meinte natürlich nur den Thread selbst.

Wir versuchen DoS-Angriffe auf RPC zwar zu verhindern bzw. abzumildern aber wenn wir uns zwischen einem anständigen Design und einer Krückenlösung die aber weniger DoS-empfindlich ist entscheiden müssen nehmen wir auf jeden Fall das anständige Design. Ich denke bei RPC geht die Schönwetter-Performance vor, den Regenschirm sollen andere Stellen halten.
Schönwetter-Performance ja, aber bitte Schlechtwetter-Robustheit. :-P

 

Einloggen