1 Punkte von GN⁺ 2024-04-28 | 1 Kommentare | Auf WhatsApp teilen

Überblick über Hubris IPC

  • Hubris verwendet einen kleinen, nicht anwendungsabhängigen Kernel, während sich der Großteil des Codes (Treiber, Anwendungslogik, Netzwerk-Stack usw.) in separat kompilierten, isolierten Tasks befindet
  • Diese Tasks können über ein Cross-Task-Messaging-System (IPC) miteinander kommunizieren
  • Das IPC von Hubris besteht aus drei Kernoperationen, die im Kernel implementiert sind: RECV, SEND, REPLY
    • RECV: Nimmt die eingehende Nachricht mit der höchsten Priorität entgegen oder blockiert, bis eine Nachricht eintrifft
    • SEND: Überträgt Nachricht und Kontrolle an den empfangenden Task und hält den Aufrufer an. Der Aufrufer wechselt in einen Wartezustand, bis er eine Antwort erhält
    • REPLY: Liefert eine Antwort an einen Task, der zuvor SEND verwendet hat, damit er seine Ausführung fortsetzen kann

Neue und interessante Fehlermodi

  • Da IPC Task-Grenzen überschreitet und die Tasks in Hubris separat kompilierte Programme sind, muss man vorsichtig sein, wenn man bei IPC dieselben Annahmen trifft, die der Compiler bei Funktionsaufrufen voraussetzt
  • Alle Tasks, die in Hubris als IPC-Server fungieren, müssen potenzielle Fehler wie die folgenden behandeln:
    • Empfang einer Nachricht mit einer ungeeigneten Operation Code für die Schnittstelle
    • Empfang eines nicht interpretierbaren Byte-Bündels statt des erwarteten Nachrichtentyps oder einer zu kurzen bzw. zu langen Nachricht
    • Empfang nicht des benötigten Speichers (z. B. beschreibbarer Speicher)

In normalen und korrekten Programmen tritt das nicht auf

  • In normalen Hubris-Programmen treten die oben genannten Situationen nicht auf
  • Tasks sind durch die Konfiguration des Build-Systems miteinander verbunden, sodass sie kaum miteinander verwechselt werden können
  • Client und Server verwenden generierten Rust-Code, sodass man annehmen kann, dass das Typsystem über Task-Grenzen hinweg funktioniert

Der Kernel erlaubt keinerlei Unsinn

  • Unter Unix führt ein Verstoß gegen die Vorbedingungen eines Systemaufrufs dazu, dass dem Aufrufer ein Fehlercode zurückgegeben wird; in Hubris wird der Task dagegen sofort zerstört
  • Der Hubris-Kernel übermittelt Fehler an Tasks, die Kernel-Regeln verletzen, über das Konzept des Synthetic Fault
  • Tritt in Hubris ein Hardware- oder Synthetic Fault auf, wird der Task sofort gestoppt und kann nicht wiederhergestellt werden

Auch der Server erlaubt keinerlei Unsinn

  • Über den dreizehnten und ungewöhnlichsten Systemaufruf, REPLY_FAULT, kann ein Server einen Fehler an den Client übermitteln
  • REPLY_FAULT ähnelt REPLY, doch statt eine Nachricht zu übermitteln und den Task wieder ausführbar zu machen, übermittelt es einen Fehler und stoppt den Task
  • Mit REPLY_FAULT lässt sich unnötige IPC-Fehlerbehandlung vermeiden. Normale Programme verhalten sich so, als könnten Probleme gar nicht auftreten, und abnormale Programme bekommen nicht einmal die Gelegenheit, Fehler zu behandeln
  • REPLY_FAULT bietet zudem eine neue Möglichkeit, anwendungsspezifische Fehler wie Zugriffskontrollregeln zu definieren und umzusetzen

Meinung von GN⁺

  • REPLY_FAULT ist ein mächtiger Mechanismus, mit dem ein Server auf dem Client einen Cross-Process-panic! auslösen kann, ohne dass der Client kooperieren muss. Dadurch wird Hubris zu einem System, das bösartigen Programmen sehr feindselig gegenübersteht
  • Ein Nachteil von REPLY_FAULT ist, dass Fuzzing sehr schwierig wird. Ein Chaos-Engineering-Task, der zufällige IPCs oder Systemaufrufe erzeugt, wird bei fast jedem Verhalten sofort zurückgesetzt
  • Da normale Hubris-Tasks IPC-Nachrichten nicht dynamisch erzeugen, können sie arbeiten, ohne sich der Existenz von REPLY_FAULT überhaupt bewusst zu sein
  • Über REPLY_FAULT kann ein Server beim Client willkürlich Fehler auslösen, doch die Bewertung davon ist noch nicht vollständig abgeschlossen
  • Die aggressive Fehlerbehandlung von Hubris hilft dabei, Fehler früh in der Entwicklung zu finden, und macht es anders als bei Fehlercodes unmöglich, Fehler einfach zu ignorieren
  • Die Verwendung einer allgemeinen Methode zur Behandlung von IPC-Fehlern kann auch bei normalen Programmen unnötigen Overhead erzeugen. REPLY_FAULT scheint dies zu vermeiden und zugleich bei abnormen Programmen strikt durchzugreifen, was wie eine elegante Lösung wirkt

1 Kommentare

 
GN⁺ 2024-04-28
Hacker-News-Kommentare

Zusammengefasst ergibt sich Folgendes:

  • Es werden Bedenken geäußert, ob sich REPLY_FAULT kaskadenartig fortpflanzt und welche Verwundbarkeiten daraus entstehen

    • In einer Situation, in der A auf B wartet und B auf C wartet, muss geprüft werden, ob bei einem REPLY_FAULT von C auch A beendet wird
    • Falls ja, könnte das gesamte System verwundbar werden
    • Wenn SEND eine zyklische Struktur bildet, könnte man sich unbeabsichtigt sogar selbst beenden
  • REPLY_FAULT bietet eine Möglichkeit, anwendungsspezifische Fehler wie Zugriffskontrolle zu definieren und zu implementieren

    • Das ist nützlich, wenn das System klein und eng gekoppelt ist und der Systemdesigner die Anwendung selbst schreibt
    • Bei der Einbindung von Drittanbieter-Code ist es jedoch bedenklich, dass die Gegenseite Prozesse jederzeit sofort beenden kann
    • Es gibt viele schlechte Treiber und Hintergrundprozesse, geschrieben von Entwicklern, die von Administratoren schikaniert werden
  • In Systemen, in denen ein Team den gesamten Code schreibt, kann das Entfernen verdächtiger Clients die Iterationsgeschwindigkeit erhöhen

  • Hubris kann als ein Kernel betrachtet werden, der Server Effekte ausführen lässt, die Clients nicht verarbeiten können

    • Das erschwert Code-Wiederverwendung und Komposition, vereinfacht aber das Ausführungsmodell
    • In statischen Embedded-Systemen könnte das der richtige Kompromiss sein
  • Hubris und Humility sind Technologien, in die man sich gern tief einarbeiten würde, was aber durch Zeit- und Verpflichtungsgrenzen erschwert wird

  • In einem Aprilscherz-RFC zu HTTP wird der HTTP-Statuscode 499 mit der Bedeutung „Sie sollten sich schämen“ vorgeschlagen

    • Das passt zu einem Kontext wie „Was zum ... aber eigentlich gar nicht schlecht“
  • Unter Verweis auf Einsteins Zitat „So einfach wie möglich, aber nicht einfacher“ wird angemerkt, dass das Design von Hubris gegen den zweiten Teil verstößt

    • An einer Betriebsumgebung, die keinerlei Unordnung der realen Welt zulässt, besteht kein Interesse
  • Humility ist ein großartiger Name für einen Debugger

    • Viele Programmierer gehen davon aus, dass „guter“ Code kein Debugging braucht, und weigern sich deshalb, einen Debugger zu verwenden
  • Unter Linux kann man nicht allein über Sockets direkt andere Programme beenden, aber mit Root-Rechten lassen sich andere Prozesse beenden oder sogar Neustarts auslösen

    • In Containern sind Root-Rechte häufig üblich, daher besteht dieses Risiko
  • Das steht in gewissem Widerspruch zur klassischen Weisheit aus Netzwerksystemen: „Sei großzügig beim Annehmen und strikt beim Senden“

    • Wenn man jedoch eine API ändert und die Kompatibilität bestehender Programme erhalten will, muss man beim Annehmen zwangsläufig großzügig sein