Was ist eine sichere Programmiersprache?

54

Sichere Programmiersprachen (PL) werden immer beliebter. Ich frage mich, was die formale Definition von sicherem PL ist. Zum Beispiel ist C nicht sicher, aber Java ist sicher. Ich vermute, dass die Eigenschaft "safe" eher auf eine PL-Implementierung als auf die PL selbst angewendet werden sollte. Wenn ja, diskutieren wir eine Definition der sicheren PL-Implementierung. Meine eigenen Versuche, diese Vorstellung zu formalisieren, führten zu einem seltsamen Ergebnis, daher würde ich gerne andere Meinungen hören. Bitte sagen Sie nicht, dass jeder PL unsichere Befehle hat. Wir können immer eine sichere Teilmenge nehmen.

beroal
quelle
Kommentare sind nicht für längere Diskussionen gedacht. Diese Unterhaltung wurde in den Chat verschoben .
Gilles 'SO- hör auf böse zu sein'
"Wir können immer eine sichere Untermenge nehmen" Wie können Sie sicher sein, dass die resultierende Sprache immer noch Turing-vollständig ist? (das ist, was in der Regel mit "Programmiersprache" gemeint ist)
effeffe
"Die Eigenschaft" safe "sollte auf eine PL-Implementierung und nicht auf die PL selbst angewendet werden" - Sie können einen PL-Safe aufrufen, wenn eine sichere Implementierung vorhanden ist.
Dmitry Grigoryev

Antworten:

17

Wenn wir eine Sprache in gewisser Hinsicht als "sicher" bezeichnen , bedeutet dies formal, dass es einen Beweis dafür gibt, dass kein gut ausgebildetes Programm in der Sprache etwas bewirken kann, das wir als gefährlich betrachten. Das Wort "sicher" wird auch weniger formal verwendet, aber das ist es, was die Leute hier unter Ihrer Frage verstehen. Es gibt viele verschiedene Definitionen von Eigenschaften, die eine „sichere“ Sprache haben soll.

Einige wichtige sind:

  • Andrew Wright und Matthias Felleisens Definition von „Type Soundness“ , die an vielen Stellen (einschließlich Wikipedia) als akzeptierte Definition von „Type Safety“ zitiert wird, und ihr Beweis aus dem Jahr 1994, dass eine Untergruppe von ML dieser Definition entspricht.

  • Michael Hicks führt hier mehrere Definitionen von „Memory Safety“ auf . Einige sind Listen von Fehlertypen, die nicht auftreten können, und andere basieren auf der Behandlung von Zeigern als Funktionen. Java garantiert, dass keiner dieser Fehler möglich ist (es sei denn, Sie verwenden ausdrücklich eine markierte Funktion unsafe), indem ein Garbage Collector alle Zuordnungen und Freigabe verwaltet. Rust leistet die gleiche Garantie (sofern Sie den Code nicht ausdrücklich als kennzeichnen unsafe) über sein affines Typsystem, bei dem eine Variable entweder im Besitz oder geliehen sein muss, bevor sie höchstens einmal verwendet wird.

  • In ähnlicher Weise wird threadsicherer Code normalerweise als Code definiert, der bestimmte Arten von Fehlern, die Threads und gemeinsamen Speicher betreffen, einschließlich Datenrassen und Deadlocks, nicht aufweisen kann. Diese Eigenschaften werden häufig auf Sprachebene erzwungen: Rust garantiert, dass in seinem Typsystem keine Datenrennen stattfinden können, C ++ garantiert, dass seine std::shared_ptrintelligenten Zeiger auf dieselben Objekte in mehreren Threads ein Objekt nicht vorzeitig löschen oder es beim letzten Verweis nicht löschen atomicWenn C und C ++ zerstört werden, verfügen sie zusätzlich über in die Sprache eingebaute Variablen, wobei atomare Operationen bei korrekter Verwendung bestimmte Arten von Speicherkonsistenz erzwingen. MPI beschränkt die Interprozesskommunikation auf explizite Nachrichten, und OpenMP verfügt über eine Syntax, mit der sichergestellt wird, dass der Zugriff auf Variablen aus verschiedenen Threads sicher ist.

  • Die Eigenschaft, dass Speicher niemals ausläuft, wird häufig als speichersicher bezeichnet. Die automatische Speicherbereinigung ist eine Sprachfunktion, um dies zu gewährleisten.

  • Viele Sprachen haben die Garantie, dass ihre Operationen zu genau definierten Ergebnissen führen und sich ihre Programme gut verhalten. Wie Supercat oben ein Beispiel gegeben hat, tut C dies für vorzeichenlose Arithmetik (garantiert sicheres Umbrechen), aber nicht für vorzeichenbehaftete Arithmetik (wobei ein Überlauf willkürliche Fehler verursachen kann, da C CPUs unterstützen musste, die beim vorzeichenbehafteten Rechnen völlig andere Dinge tun Überläufe), aber dann konvertiert die Sprache manchmal unbemerkt vorzeichenlose Mengen in signierte.

  • Funktionale Sprachen haben eine große Anzahl von Invarianten, von denen garantiert wird, dass jedes wohlgeformte Programm beibehält, beispielsweise dass reine Funktionen keine Nebenwirkungen verursachen können. Diese können oder können nicht als "sicher" beschrieben werden.

  • Einige Sprachen, wie SPARK oder OCaml, sollen den Nachweis der Programmkorrektheit erleichtern. Dies kann oder kann nicht als "sicher" vor Fehlern beschrieben werden.

  • Beweise, dass ein System nicht gegen ein formales Sicherheitsmodell verstoßen kann (daher der Spruch: "Jedes System, das nachweislich sicher ist, ist es wahrscheinlich nicht.")

Davislor
quelle
1
Dies kann oder kann nicht als "sicher" vor Fehlern beschrieben werden. Würden Sie das bitte etwas näher erläutern? Was meinst du mit "von Bugs"?
Scaaahu
2
@scaaahu Hier ist ein Beispiel für eine Website, auf der Software als „nachweislich sicher“ bezeichnet wird. In diesem Zusammenhang handelt es sich um Software, mit der Kollisionen von Flugzeugen verhindert werden und die somit vor Kollisionen geschützt ist.
Davislor
1
Ich akzeptiere diese Antwort, weil sie Sicherheitsarten auflistet. Der Typ, an den ich gedacht habe, ist Speichersicherheit.
beroal
Während diese Antwort einige hilfreiche Links und viele Beispiele auflistet, sind die meisten von ihnen völlig durcheinander. Garbage Collection stellt sicher, dass der Speicher niemals ausläuft oder dass nicht automatisch "unsichere" Blöcke verwendet werden. Dies gibt Ihnen Sicherheit oder einen signierten Überlauf. Undefiniertes Verhalten, weil Compiler einige seltsame CPUs unterstützen müssen. Und nur ein kurzes Wort für Ada / SPARK, die einzige der genannten Sprachen, die die Sicherheit ernst nimmt.
VTT
93

Es gibt keine formale Definition für "sichere Programmiersprache". Es ist eine informelle Vorstellung. Sprachen, die behaupten, Sicherheit zu bieten, geben in der Regel eine genaue formelle Aussage darüber, welche Art von Sicherheit beansprucht / garantiert / bereitgestellt wird. Beispielsweise kann die Sprache Typensicherheit, Speichersicherheit oder eine ähnliche Garantie bieten.

DW
quelle
13
Als Addeumdum, wenn wir über C vs Java wie OPs Post sprechen: Es ist die Speichersicherheit, die in Java und nicht in C gewährleistet ist. Die Typensicherheit wird von beiden auf ihre eigene Weise bereitgestellt. (Ja, viele Leute, die dies lesen, wissen das bereits, aber manche wissen es vielleicht nicht.)
Walfrat
17
@Walfrat Das gehört dazu. Java hat auch kein undefiniertes Verhalten, was wir von einer Sprache erwarten, die sich "sicher" nennt. Was Typsysteme betrifft, glaube ich nicht, dass ein starkes statisches Typsystem das ist, was die Leute mit "sicher" meinen. Dynamisch getippte Sprachen wie Python sind im Allgemeinen doch "sicher".
Max Barraclough
2
Meine Definition der Typensicherheit ist die Kompilierungsprüfung, die damit umgeht. Das ist möglicherweise nicht die formale Definition. Beachten Sie, dass ich "Typ Sicherheit" sagte, nicht "sicher". Für mich bezieht sich "sichere" Sprache auf "meine" Definition von "Typ- und Speichersicherheit", und ich denke, dass dies die am weitesten verbreitete ist. Natürlich spreche ich nicht von einer Falle wie Reflection / Void Pointer in C, die die Kompilierung nicht bewältigen kann. Eine andere mögliche Definition von safe sind Programme, die nicht mit einem Segmentfehler abstürzen, wie der unitialisierte Zeiger in C. Solche Dinge werden im Allgemeinen in Python und Java gewährt.
Walfrat
7
@Walfrat Alles, was Sie bekommen, ist eine Sprache, in der die Syntax gut definiert ist. Es garantiert nicht, dass die Ausführung gut definiert ist - und wie oft ich einen JRE-Absturz gesehen habe, kann ich Ihnen mit Bestimmtheit sagen, dass es als System nicht "sicher" ist. Auf der anderen Seite hat MISRA in C alles daran gesetzt, undefiniertes Verhalten zu vermeiden, um eine sicherere Teilmenge der Sprache zu erhalten, und die Kompilierung von C in Assembler ist viel besser definiert. Es kommt also darauf an, was Sie für "sicher" halten.
Graham
5
@MaxBarraclough - "Java hat auch kein undefiniertes Verhalten" - Java hat kein undefiniertes Verhalten in dem Sinne, wie es in den C-Spezifikationen in der Sprachdefinition verwendet wird (obwohl es einigen Code erlaubt, Werte zu erzeugen, die keinen einzigen vordefinierten Wert haben, z. B. Zugriff Eine Variable, die in einem anderen Thread geändert wird, oder durch Zugriff auf ein doubleoder longwährend sie in einem anderen Thread geändert wird, wobei nicht garantiert wird, dass nicht die Hälfte eines Werts auf eine nicht angegebene Weise mit der Hälfte eines anderen Werts gemischt wird, sondern die API-Spezifikation hat jedoch undefiniertes Verhalten in einigen Fällen.
Jules
41

Wenn Sie ein Exemplar von Benjamin Pierces Types and Programming Languages in den Händen halten können , hat er in der Einleitung einen guten Überblick über verschiedene Perspektiven des Begriffs "sichere Sprache".

Eine vorgeschlagene Interpretation des Begriffs, die Sie vielleicht interessieren könnte, ist:

„Eine sichere Sprache wird vollständig durch das Handbuch ihres Programmierers definiert.“ Die Definition einer Sprache ist die Menge von Dingen, die der Programmierer verstehen muss, um das Verhalten jedes Programms in der Sprache vorherzusagen. Dann stellt das Handbuch für eine Sprache wie C keine Definition dar, da das Verhalten einiger Programme (z. B. Programme mit ungeprüften Arrayzugriffen oder Zeigerarithmetik) nicht vorhergesagt werden kann, ohne die Details zu kennen, wie ein bestimmter C-Compiler Strukturen im Speicher anordnet usw., und dasselbe Programm kann ganz unterschiedliche Verhaltensweisen aufweisen, wenn es von verschiedenen Compilern ausgeführt wird.

Daher würde ich zögern, den Begriff "unsicher" für eine Programmiersprachenimplementierung zu verwenden. Wenn ein undefinierter Begriff in einer Sprache in verschiedenen Implementierungen ein unterschiedliches Verhalten hervorruft, kann eine der Implementierungen zu einem Produktverhalten führen, das möglicherweise zu erwarten ist, ich würde es jedoch nicht als "sicher" bezeichnen.

martin
quelle
7
Das Problem des Anhaltens besagt natürlich, dass es unabhängig von der Sprache immer Programme geben wird, deren Verhalten anhand der Sprachdefinition nicht vorhersehbar ist. Jede Definition, die vom "Vorhersagen des Verhaltens jedes Programms in der Sprache" abhängt, ist für jede Turing-vollständige Sprache grundlegend fehlerhaft.
MSalters
15
@MSalters Dies ist ein weit verbreitetes Missverständnis des Halteproblems. Die Unentscheidbarkeit des Halteproblems impliziert, dass es unmöglich ist, das Verhalten eines beliebigen Programms in einer Turing-vollständigen Sprache mechanisch abzuleiten . Es ist aber möglich, dass für jedes gegebene Programm das Verhalten vorhersehbar ist. Es ist nur so, dass Sie kein Computerprogramm erstellen können , das diese Vorhersage trifft.
Gilles 'SO- hör auf böse zu sein'
7
@Giles: Das ist nicht der Fall. Angenommen, für jedes nicht beendende Programm existiert ein Nachweis der Nichtbeendigung. Anschließend können Sie die Beweise für die Nichtbeendigung auflisten, um festzustellen, ob ein bestimmtes Programm angehalten hat. Das Halteproblem ist also entscheidend. Widerspruch. Daher sind einige nicht beendende Programme nachweislich nicht beendbar.
Kevin
9
@Gilles: Mir ist völlig bewusst, dass viele Programme nachweislich stehen bleiben oder nicht. Die Aussage hier bezieht sich jedoch buchstäblich auf das Verhalten jedes Programms. Der Beweis des Halting-Theorems zeigt, dass es mindestens ein Programm gibt, für das dies nicht zutrifft. Es ist nur ein nicht konstruktiver Beweis, der Ihnen nicht sagt, welches Programm unentscheidbar ist.
MSalters
8
@MSalters Ich denke, das implizite ist, dass es sich bei der Aussage eher um das kleinräumige Verhalten des Programms als um großräumiges, auftauchendes Verhalten handelt. Nehmen wir zum Beispiel die Collatz-Vermutung . Die einzelnen Schritte des Algorithmus sind einfach und klar definiert, aber das aufkommende Verhalten (wie viele Iterationen bis zum Anhalten und wenn überhaupt) ist alles andere als klar. - "Predict" wird hier informell verwendet. Es könnte besser geschrieben werden als "wissen, wie eine bestimmte Anweisung in einem beliebigen Programm ausgeführt wird".
RM
18

Safe ist keine Binärdatei, sondern ein Kontinuum .

Informell ausgedrückt bedeutet Sicherheit die Abwehr von Fehlern, wobei die 2 am häufigsten genannten sind:

  • Speichersicherheit: Die Sprache und ihre Implementierung verhindern eine Vielzahl von Speicherfehlern, z. B. nachträglicher, doppelter, unzulässiger Zugriff, ...
  • Typensicherheit: Die Sprache und ihre Implementierung verhindern eine Vielzahl typenbezogener Fehler wie ungeprüfte Casts, ...

Dies sind nicht die einzigen Klassen von Fehlern, die von Sprachen verhindert werden. Die Freiheit von Datenrassen oder Deadlocks ist eher wünschenswert. Korrektheitsbeweise sind ziemlich süß, usw.

Einfach sind falsche Programme selten jedoch „unsicher“ angesehen (nur Buggy), und der Begriff Sicherheit wird im Allgemeinen für Garantien reserviert unsere Fähigkeit beeinflussen zu folgern , ein Programm zu. Daher sind C, C ++ oder Go mit undefiniertem Verhalten unsicher.

Und natürlich gibt es Sprachen mit unsicheren Teilmengen (Java, Rust, ...), die gezielt Bereiche abgrenzen, in denen der Entwickler für die Einhaltung der Sprachgarantien verantwortlich ist und der Compiler sich im "hands-off" -Modus befindet. Trotz dieser pragmatischen Definition werden die Sprachen im Allgemeinen immer noch als sicher bezeichnet .

Matthieu M.
quelle
7
Ich würde sagen, es ist ein Gitter.
PatJ
1
Die meisten Implementierungen von Programmiersprachen weisen unsichere Funktionen auf (z Obj.magic. B. in Ocaml). Und in der Praxis sind diese wirklich notwendig
Basile Starynkevitch
4
@ BasileStarynkevitch: In der Tat. Ich würde denken, dass jede Sprache mit FFI notwendigerweise ein gewisses Maß an Unsicherheit enthält, da das Aufrufen einer C-Funktion das "Pining" von GC-Objekten erfordert und manuell sicherstellt, dass die Signaturen auf beiden Seiten übereinstimmen.
Matthieu M.
15

Obwohl ich der Antwort von DW nicht widerspreche, denke ich, dass ein Teil von "safe" unadressiert bleibt.

Wie bereits erwähnt, gibt es mehrere Arten von Sicherheitsmaßnahmen. Ich glaube, es ist gut zu verstehen, warum es mehrere Begriffe gibt. Jeder Begriff ist mit der Idee verbunden, dass Programme besonders unter einer bestimmten Klasse von Fehlern leiden und dass Programmierer diese Art von Fehlern nicht erzeugen können, wenn die Sprache den Programmierer daran hindert.

Es sollte beachtet werden, dass diese unterschiedlichen Begriffe daher unterschiedliche Klassen von Fehlern haben und diese Klassen sich weder gegenseitig ausschließen noch alle Formen von Fehlern abdecken. Die Frage, ob ein bestimmter Speicherort ein bestimmtes Objekt enthält, ist, um nur zwei Beispiele von DW zu nennen, sowohl eine Frage der Typensicherheit als auch der Speichersicherheit.

Eine weitere Kritik an "sicheren Sprachen" ergibt sich aus der Beobachtung, dass das Verbot bestimmter als gefährlich erachteter Konstrukte den Programmierer dazu zwingt, Alternativen zu finden. Empirisch wird Sicherheit durch gute Bibliotheken besser erreicht. Die Verwendung von Code, der bereits in der Praxis getestet wurde, erspart Ihnen das Auftreten neuer Fehler.

MSalters
quelle
10
Für diese Site ist es eher unangebracht, da Software-Engineering eigentlich keine Wissenschaft ist, aber ich bin mit Ihrer empirischen Aussage nicht einverstanden. Wenn Sie gute Bibliotheken verwenden, werden Sie nicht in unsicheren Sprachen gespeichert, da Sie nicht davor geschützt sind, sie falsch zu verwenden. Mit sicheren Sprachen erhalten Sie mehr Garantien vom Bibliotheksautor und können sicherer sein, dass Sie sie korrekt verwenden.
Gilles 'SO- hör auf böse zu sein'
3
Ich bin mit MSalters dabei. - "Mit sicheren Sprachen erhalten Sie mehr Garantien vom Bibliotheksautor und können sicherer sein, dass Sie sie korrekt verwenden." Dies ist keine Folge für alle praktischen Zwecke.
Captain Giraffe
9

Ein grundlegender Unterschied zwischen C und Java besteht darin, dass, wenn man bestimmte leicht identifizierbare Merkmale von Java vermeidet (z. B. jene im UnsafeNamespace), jede mögliche Aktion, die man versuchen kann - einschließlich "fehlerhafter" -, einen begrenzten Bereich möglicher Ergebnisse hat . Während dies Unsafedie Möglichkeiten in Java einschränkt - zumindest ohne den Namespace zu verwenden -, ist es auch möglich, den Schaden zu begrenzen, der durch ein fehlerhaftes Programm oder - was noch wichtiger ist - durch ein Programm verursacht werden kann, das korrekt verarbeitet wird gültige Dateien, wird jedoch nicht besonders gegen fehlerhafte Dateien geschützt.

Herkömmlicherweise verarbeiteten C-Compiler viele Aktionen in "normalen" Fällen auf standardmäßige Weise, während sie viele Eckfälle "auf eine für die Umgebung charakteristische Weise" verarbeiteten. Wenn man eine CPU verwendet, die bei einem numerischen Überlauf einen Kurzschluss aufweist und Feuer fängt und verhindern möchte, dass die CPU Feuer fängt, muss man Code schreiben, um einen numerischen Überlauf zu vermeiden. Wenn man jedoch eine CPU verwendet, die Werte auf Zweierkomplement-Weise perfekt glücklich abschneidet, muss man Überläufe nicht vermeiden, wenn ein solches Abschneiden zu einem akzeptablen Verhalten führen würde.

Modernes C geht noch einen Schritt weiter: Selbst wenn man auf eine Plattform abzielt, die natürlich ein Verhalten für einen numerischen Überlauf definiert, für den der Standard keine Anforderungen auferlegt, kann ein Überlauf in einem Teil eines Programms das Verhalten anderer Teile des Programms beeinflussen Programm in willkürlicher Weise, die nicht an die Gesetze der Zeit und der Kausalität gebunden ist. Betrachten Sie zum Beispiel Folgendes:

 uint32_t test(uint16_t x)
 {
   if (x < 50000) foo(x);
   return x*x; // Note x will promote to "int" if that type is >16 bits.
 }

Ein "moderner" C-Compiler, dem so etwas wie das Obige gegeben ist, könnte zu dem Schluss kommen, dass er den Aufruf von "foo" bedingungslos ausführen kann, da die Berechnung von x * x überlaufen würde, wenn x größer als 46340 ist. Beachten Sie, dass selbst dann, wenn es akzeptabel wäre, ein Programm abnormal zu beenden, wenn x außerhalb des Bereichs liegt, oder wenn die Funktion in solchen Fällen einen beliebigen Wert zurückgibt, der Aufruf von foo () mit einem außerhalb des Bereichs liegenden x weit darüber hinausgehende Schäden verursachen kann eine dieser Möglichkeiten. Herkömmliches C würde keine Sicherheitsausrüstung bieten, die über die vom Programmierer und der zugrunde liegenden Plattform gelieferten hinausgeht, aber Sicherheitsausrüstung würde den Schaden durch unerwartete Situationen begrenzen. Modern C umgeht jede Sicherheitsausrüstung, die nicht 100% effektiv ist, um alles unter Kontrolle zu halten.

Superkatze
quelle
3
@ DavidThornley: Vielleicht war mein Beispiel zu subtil. Wenn intes 32 Bits sind, xwird es zu signiert befördert int. Nach der Begründung zu urteilen, erwarteten die Autoren des Standards, dass nicht-seltsame Implementierungen signierte und nicht signierte Typen in äquivalenter Weise außerhalb einiger spezieller Fälle behandeln würden, aber gcc "optimiert" manchmal auf eine Weise, die bricht, wenn a uint16_tdurch uint16_tMultiplizieren ein Ergebnis jenseits von INT_MAX ergibt , auch wenn das Ergebnis als vorzeichenloser Wert verwendet wird.
Supercat
4
Gutes Beispiel. Dies ist ein Grund, warum wir immer (auf GCC oder Clang) kompilieren sollten -Wconversion.
Davislor
2
@Davislor: Ah, ich habe gerade bemerkt, dass Godbolt die Reihenfolge, in der die Compilerversionen aufgeführt sind, umgekehrt hat. Wenn Sie also die letzte Version von gcc in der Liste auswählen, erhalten Sie die neueste und nicht die früheste. Ich denke nicht, dass die Warnung besonders hilfreich ist, da sie dazu neigt, viele Situationen return x+1;zu kennzeichnen, die nicht problematisch sein sollten, und das Ergebnis auf uint32_t zu setzen, würde die Nachricht ersticken, ohne das Problem zu beheben.
Supercat
2
@supercat Das Eliminieren von Tests ist sinnlos, wenn der Compiler die Tests an einem anderen Ort ablegen muss.
user253751
3
@immibis: Mit einer "checked assume" -Anweisung kann ein Compiler möglicherweise viele Tests ersetzen oder eine Prüfung, die häufig innerhalb einer Schleife durchgeführt wird, durch einen einzelnen Test, der möglicherweise außerhalb einer Schleife ausgeführt wird. Das ist besser, als Programmierer zu verpflichten, Überprüfungen hinzuzufügen, die im Maschinencode nicht erforderlich wären, damit ein Programm die Anforderungen erfüllt, um sicherzustellen, dass ein Compiler die zur Erfüllung der Anforderungen erforderlichen Überprüfungen nicht "herausoptimiert".
Supercat
7

In einer Sprache gibt es mehrere Ebenen der Korrektheit. In der Reihenfolge der zunehmenden Abstraktion:

  • Nur wenige Programme sind fehlerfrei (nur solche, für die Richtigkeit nachgewiesen werden kann). Andere haben bereits erwähnt, dass die Fehlerbegrenzung daher der konkreteste Sicherheitsaspekt ist. Sprachen, die in einer virtuellen Maschine wie Java und .net ausgeführt werden, sind in dieser Hinsicht im Allgemeinen sicherer: Programmfehler werden normalerweise auf definierte Weise abgefangen und behandelt. 1
  • Auf der nächsten Ebene machen Fehler, die zur Kompilierungszeit statt zur Laufzeit erkannt wurden, eine Sprache sicherer. Ein syntaktisch korrektes Programm sollte auch semantisch so gut wie möglich stimmen. Natürlich kann der Compiler das Gesamtbild nicht erkennen, daher betrifft dies die Detailebene. Starke und ausdrucksstarke Datentypen sind ein Aspekt der Sicherheit auf dieser Ebene. Man könnte sagen, die Sprache sollte es schwierig machen, bestimmte Arten von Fehlern zu machen(Tippfehler, nicht gebundener Zugriff, nicht initialisierte Variablen usw.). Laufzeit-Typinformationen wie Arrays, die Längeninformationen enthalten, vermeiden Fehler. Ich programmierte Ada 83 im College und stellte fest, dass ein kompilierendes Ada-Programm typischerweise eine Größenordnung weniger Fehler enthielt als das entsprechende C-Programm. Nehmen Sie einfach Adas Fähigkeit, ganzzahlige Typen zu definieren, die ohne explizite Konvertierung nicht zugewiesen werden können: Ganze Raumschiffe sind abgestürzt, weil Füße und Meter verwechselt wurden, was man mit Ada trivial vermeiden könnte.

  • Auf der nächsten Ebene sollte die Sprache Mittel bereitstellen, um Kesselzeichencode zu vermeiden. Wenn Sie Ihre eigenen Container oder deren Sortierung oder Verkettung schreiben müssen oder wenn Sie Ihre eigenen schreiben müssen, werden string::trim()Sie Fehler machen. Da die Abstraktionsebene ansteigt, bezieht dieses Kriterium die eigentliche Sprache sowie die Standardbibliothek der Sprache ein.

  • Heutzutage sollte die Sprache Mittel zur gleichzeitigen Programmierung auf Sprachebene bereitstellen. Parallelität ist schwer zu finden und ohne Sprachunterstützung möglicherweise nicht möglich.

  • Die Sprache sollte Mittel zur Modularisierung und Zusammenarbeit bieten. Die starken, durchdachten, benutzerdefinierten Typen von oben tragen zur Erstellung aussagekräftiger APIs bei.

Etwas orthogonal sollte die Sprachdefinition verständlich sein; Die Sprache und die Bibliotheken sollten gut dokumentiert sein. Eine fehlerhafte oder fehlende Dokumentation führt zu fehlerhaften und falschen Programmen.


1 Da sich die Richtigkeit der virtuellen Maschine jedoch in der Regel nicht nachweisen lässt, sind solche Sprachen möglicherweise paradoxerweise nicht für sehr strenge Sicherheitsanforderungen geeignet.

Peter - Setzen Sie Monica wieder ein
quelle
1
+1 Für klare Schicht für Schicht Erklärung. Eine Frage an Sie: Ganze Raumschiffe sind abgestürzt, weil Füße und Meter verwechselt wurden, was man mit Ada trivial vermeiden konnte. , reden Sie über Mars-Sonde, die aufgrund eines einfachen Rechenfehlers verloren gegangen ist ? Kennst du zufällig die Sprache, die sie für dieses Raumschiff verwendeten?
Scaaahu
2
@scaaahu Ja, ich denke ich habe mich darauf bezogen. Nein, ich kenne die Sprache nicht. Beim Lesen des Berichts scheint es, dass die von der Sonde gesendeten Daten von einer Software auf der Erde verarbeitet wurden, wobei eine Datendatei erstellt wurde, die dann zur Bestimmung der Schubwerte verwendet wurde. Einfache Spracheingabe ist in diesem Szenario nicht anwendbar. Übrigens hatten sie mehrere Probleme mit der Bodensoftware und dem Datendateiformat, eine Verwirrung, die eine frühzeitige Erkennung des Problems verhinderte. Die Geschichte ist also kein direktes Argument für starkes Tippen, aber dennoch eine warnende Geschichte.
Peter - Reinstate Monica
1

Bitte sagen Sie nicht, dass jeder PL unsichere Befehle hat. Wir können immer eine sichere Teilmenge nehmen.

Jede Sprache, die ich kenne, hat Möglichkeiten, illegale Programme zu schreiben, die kompiliert und ausgeführt werden können. Und jede Sprache, die ich kenne, hat eine sichere Untergruppe. Also, was ist deine Frage wirklich?


Sicherheit ist mehrdimensional und subjektiv.

Einige Sprachen haben viele Operationen, die "unsicher" sind. Andere haben weniger solche Operationen. In einigen Sprachen ist die Standardmethode von Natur aus unsicher. In anderen Fällen ist die Standardeinstellung sicher. In einigen Sprachen gibt es eine explizite "unsichere" Teilmenge. In anderen Sprachen gibt es überhaupt keine solche Untergruppe.

In einigen Sprachen bezieht sich "Sicherheit" ausschließlich auf die Speichersicherheit - ein Dienst, der von der Standardbibliothek und / oder der Laufzeit angeboten wird, bei dem Speicherzugriffsverletzungen erschwert oder unmöglich gemacht werden. In anderen Sprachen umfasst "Sicherheit" ausdrücklich die Thread-Sicherheit. In anderen Sprachen bezieht sich "Sicherheit" auf die Garantie, dass ein Programm nicht abstürzt (eine Anforderung, die es einschließt, nicht abgefangene Ausnahmen jeglicher Art nicht zuzulassen). Schließlich bezieht sich "Sicherheit" in vielen Sprachen auf die Typensicherheit - wenn das Typensystem auf bestimmte Weise konsistent ist, spricht man von "Sound" (im Übrigen haben Java und C # keine vollständig soliden Typensysteme).

In einigen Sprachen werden alle unterschiedlichen Bedeutungen von "Sicherheit" als Teilmengen der Typensicherheit betrachtet (z. B. erreichen Rust und Pony die Fadensicherheit durch Eigenschaften des Typensystems).

Theodoros Chatzigiannakis
quelle
-1

Diese Antwort ist etwas weiter gefasst. In den letzten Jahrzehnten wurden die Wörter "safe" und "safety" von bestimmten politisch orientierten Teilen der englischsprachigen Gesellschaft verstümmelt, so dass ihre gemeinsame Verwendung fast keine Definition hat. Für technische Themen definiere ich jedoch immer noch "Sicherheit" und "sicher" als: ein Gerät, das die unbeabsichtigte Verwendung von etwas verhindert oder die versehentliche Verwendung wesentlich erschwert, und den Zustand, unter dem Schutz eines solchen Geräts zu stehen .
Eine sichere Sprache hat also ein Mittel, um eine bestimmte Klasse von Fehlern einzuschränken. Natürlich sind Grenzen in einigen Fällen mit Unannehmlichkeiten oder sogar Unfähigkeit verbunden, und das heißt nicht, dass "unsichere" Sprachen zu Fehlern führen. ZB habe ich keine Sicherheitskorken an den Gabeln und habe es jahrzehntelang ohne viel Aufwand geschafft, mein Auge beim Essen nicht zu erstechen. Mit Sicherheit weniger Aufwand als mit den Korken. Sicherheit ist also mit Kosten verbunden, an denen sie gemessen werden muss. (Korkgabel ist eine Referenz auf einen Steve Martin Charakter)

Maximale Kraft
quelle