Geschwindigkeit vs. Sicherheit



  • Was z.B. ein sehr witziges Feature ist: Ich kann einer Variablen sagen, welchen Wertebereich sie haben darf (nein, nicht über den Typ 😉 ). So kann ich bei einer Integer-Variable sagen, sie darf Werte zw. -5 und 5 annehmen. Alles andere löst einen Sicherheitsfehler aus.
    Automatische Index-Checks sowie Checks auf Speicher-Zugriffsfehler sind natürlich auch dabei. Kann auch alles deaktiviert werden.

    Außerdem eignet sich die Sprache sehr gut für die automatische Verifikation von Programmen.
    Ja, ich glaube Ada ist wirklich sehr gut durchdacht, nur die Syntax is etwas schräg, aber immer noch besser als bei LISP 😃



  • GPC schrieb:

    Was z.B. ein sehr witziges Feature ist: Ich kann einer Variablen sagen, welchen Wertebereich sie haben darf (nein, nicht über den Typ 😉 ). So kann ich bei einer Integer-Variable sagen, sie darf Werte zw. -5 und 5 annehmen. Alles andere löst einen Sicherheitsfehler aus.

    Und was ist daran "besser" als an einer Realisierung durch nen eigenen Typ?



  • byto schrieb:

    GPC schrieb:

    Was z.B. ein sehr witziges Feature ist: Ich kann einer Variablen sagen, welchen Wertebereich sie haben darf (nein, nicht über den Typ 😉 ). So kann ich bei einer Integer-Variable sagen, sie darf Werte zw. -5 und 5 annehmen. Alles andere löst einen Sicherheitsfehler aus.

    Und was ist daran "besser" als an einer Realisierung durch nen eigenen Typ?

    Na ja, es ist halt sauber und einfach in die Sprache integriert und daher ohne viel Aufwand zu realisieren. Wenn ich z.B. in C++ den Wertebereich (sagen wir grad mal -5 und 5) einschränken will, ist das schon aufwändiger.



  • Also ein...

    type WINKEL is delta 0.01 range 0.0..360.0;
    

    hat schon was 😋



  • Shade Of Mine schrieb:

    wie checkst du das zur compile time?

    Die Behauptung war nicht, zur compile-Zeit auf null prüfen zu können. Aber man kann immer verhindern, dass irgendwo unerwartet null steht. Dazu musst es zwei Zeigertypen geben, einen Foo und einen Foo? und nur der Foo? darf null sein. Foo kann immer implizit in ein Foo? umgewandelt werden, aber umgekehrt muss "gecasted" werden.

    Der Compiler könnte bei so einem Cast einen auch zwingen, Code für den Fall zu schreiben, dass der Pointer null ist (ähnlich wie checked Exceptions). Das wäre supergeil.



  • groovemaster schrieb:

    Optimizer schrieb:

    Das geht über den Speicherschutz der Hardware.

    Aber auch nur, wenn auf der entsprechenden Hardware einer vorhanden ist, gelle? 🙂

    Wenn keiner vorhanden ist, dann muss der Test explizit vom JIT-compiler eingebaut werden. Aber gerade ohne Speicherschutz sind noch strengere Tests durchaus zu befürworten. Ohne Speicherschutz und ohne Garantien wie sie beispielsweise Java bietet, könntest du kein sicheres Betriebssystem schreiben.

    Optimizer schrieb:

    Tatsächlich findet sie sowieso "in beiden Sprachen" immer statt

    Ich kann nur von C++ sprechen, und da ist "immer" übertrieben. Man macht es eigentlich nur, wenn Zeiger über Schnittstellen geschickt werden (und entsprechend dokumentiert sind), oder ein Zeiger erstmal default auf Null gesetzt wird und später eine gültige Adresse zugewiesen bekommt. Was man allerdings möglichst vermeiden sollte, und schön RAII verwenden sollte. Ansonsten hat man immer einen gültigen Zeiger (nicht initialisierte jetzt mal aussen vor gelassen).

    Du verstehst nicht ganz, worauf ich hinauswollte: Weder du noch der Compiler bauen Prüfungen beim Speicherzugriff ein. Das macht die MMU des Prozessors und das Betriebssystem hat als einziges Programm das Recht, den Schutz abzustellen. Dass du einen 0-Pointer nicht dereferenzieren kannst liegt daran, weil bei 0 das Textsegment steht und das Betriebssystem dem Prozessor sagt "nur innerhalb dieser Segmente darfste das und das". Den Test hast du immer und kannst dich nicht dagegen wehren.

    Schlägt jetzt so eine Prüfung fehl, wird der entsprechende Interrupt-Handler vom BS abgearbeitet. Das kann benutzt werden, um in C++ Programmen eine access violation anzuzeigen (anstatt den Prozess gleich zu beenden) - oder in Java eine NullPointerException. Das kostet "nichts", weil du es sowieso immer bezahlst. Ausnahmen sind die seltenen Fälle wo der Speicherschutz nicht so schnell greifen würde wie eine explizite Prüfung auf null.



  • Optimizer schrieb:

    Du verstehst nicht ganz, worauf ich hinauswollte: Weder du noch der Compiler bauen Prüfungen beim Speicherzugriff ein. Das macht die MMU des Prozessors und das Betriebssystem hat als einziges Programm das Recht, den Schutz abzustellen.

    Ja, das interessiert dich als Hochsprachenprogrammierer aber überhaupt nicht. Java garantiert dir eine entsprechende Exception, deshalb müssen auch die Voraussetzungen dafür geschaffen werden. Darum schrieb ich, dass dort generell ein Overhead da ist, egal ob sich das nun tatsächlich +performacetechnisch niederschlägt oder nicht, weil die CPU zB einen hardwareseitigen Schutz hat. In C++ gibt es diese Restriktionen eben nicht. Da hast du minimal keinen Overhead, weder soft- noch hardwareseitig. Weil deine CPU dies zB nicht unterstützt, und auch softwareseitig keine Prüfung notwendig ist (zB weil du dich auf das Ergebnis von new verlassen kannst). Bis maximal 2mal Overhaed, soft- und hardwareseitig. Allerdings kann man dort, natürlich leider nur plattformabhängig, die Funktionalität des Betriebssystems ausnutzen, so dass keine explizite Prüfung mehr notwendig ist.



  • In C++ gibt es diese Restriktionen eben nicht. Da hast du minimal keinen Overhead, weder soft- noch hardwareseitig.

    Das mag für das Programmieren von Mikro-Controllern noch stimmen, auf einem typischen Desktop-System aber brauchst du ohne Wenn und Aber ein sicheres Betriebssystem. Und genau wenn man so weit schon ist, dass man ein sicheres Betriebssystem braucht, kann der Ansatz von C++ sich zum Performance-Nachteil entwickeln.

    ftp://ftp.research.microsoft.com/pub/tr/TR-2006-43.pdf schrieb:

    These mechanisms can incur non-trivial performance costs. Mapping
    from virtual to physical addresses can incur overheads up to 10–
    30% due to exception handling, inline TLB lookup, TLB reloads,
    and maintenance of kernel data structures such as page tables
    [28]. In addition, virtual memory and privilege levels increase the
    cost of inter-process communication.

    Umgekehrt können Garantien die dir Sprachen wie Java bieten, ausgenutzt werden, um eine höhere Performance zu erreichen. Der Punkt dabei ist, dass man auf Grund dieser Garantien auf ein Stück Überwachung verzichten kann. Ein sehr interessantes Forschungsprojekt dazu ist IMHO Singularity, ein Betriebssystem von Microsoft, welches auf den Hardware-seitigen Schutz der Prozesse gegeneinander verzichtet. Das ermöglicht eine deutlich schnellere Interprozess-Kommunikation und performante Mikrokernel-Architektur (d.h. auch Treiber laufen in eigenen abgeschotteten Prozessen, was die Sicherheit weiter erhöht).

    Das Betriebssystem soll aber keineswegs ohne Schutz sein. Microsoft nennt das, was man heute durch Speichersegmentierung und Schutz durch die MMU erreicht "Hardware isolated processes" und stellt dem "Software isolated processes" gegenüber, die die Eigenschaften von "sicheren" Programmiersprachen ausnutzen um nur Speicherzugriffe zu überwachen, auf die es ankommt. Ausführbare Programme liegen in .Net-Code vor und laufen praktisch ohne Speicherschutz ab.



  • Optimizer schrieb:

    Umgekehrt können Garantien die dir Sprachen wie Java bieten, ausgenutzt werden, um eine höhere Performance zu erreichen. Der Punkt dabei ist, dass man auf Grund dieser Garantien auf ein Stück Überwachung verzichten kann. Ein sehr interessantes Forschungsprojekt dazu ist IMHO Singularity, ein Betriebssystem von Microsoft, welches auf den Hardware-seitigen Schutz der Prozesse gegeneinander verzichtet. Das ermöglicht eine deutlich schnellere Interprozess-Kommunikation und performante Mikrokernel-Architektur (d.h. auch Treiber laufen in eigenen abgeschotteten Prozessen, was die Sicherheit weiter erhöht).

    Singularity ist doch genau das Gegenteil einer µKernel-Architektur. Anstelle nur wenig Code im kritischen Bereich (Ring0) einzusetzen, packen die da nicht nur eine komplette VM rein, sondern auch alle Treiber und Programme die ausgeführt werden. Das der "mehr Code" -> "mehr Sicherheits"-Ansatz fundamentale Fehler hat, sieht man ja allein an den Sicherheitslücken, der Java VMs.

    Der µKernel hat ja das Prinzip möglichst wenig Code in Ring0 platzieren, so das man den Code leicht prüfen kann.



  • Singularity ist genau die µKernel-Architektur. Du darfst dich nicht dadurch täuschen lassen, dass der Hardwareschutz für alle Prozesse ausgeschaltet ist, die Prozesse können auf Grund der genannten Garantien ihren Speicherbereich trotzdem nicht verlassen. Der Schutz ist genauso vollkommen wie man es sich nur wünschen kann. In Singularity laufen auch Treiber als eigene Prozesse und man kann mit ihnen nur über die Interprozess-Kommunikation kommunizieren (was dort aber sehr schnell ist). Das ist µKernel par excellence.


Anmelden zum Antworten