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.
programming-languages
beroal
quelle
quelle
Antworten:
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 kennzeichnenunsafe
) ü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_ptr
intelligenten Zeiger auf dieselben Objekte in mehreren Threads ein Objekt nicht vorzeitig löschen oder es beim letzten Verweis nicht löschenatomic
Wenn 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.")
quelle
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.
quelle
double
oderlong
wä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.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:
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.
quelle
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:
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 .
quelle
Obj.magic
. B. in Ocaml). Und in der Praxis sind diese wirklich notwendigObwohl 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.
quelle
Ein grundlegender Unterschied zwischen C und Java besteht darin, dass, wenn man bestimmte leicht identifizierbare Merkmale von Java vermeidet (z. B. jene im
Unsafe
Namespace), jede mögliche Aktion, die man versuchen kann - einschließlich "fehlerhafter" -, einen begrenzten Bereich möglicher Ergebnisse hat . Während diesUnsafe
die 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:
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.
quelle
int
es 32 Bits sind,x
wird es zu signiert befördertint
. 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 auint16_t
durchuint16_t
Multiplizieren ein Ergebnis jenseits von INT_MAX ergibt , auch wenn das Ergebnis als vorzeichenloser Wert verwendet wird.-Wconversion
.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.In einer Sprache gibt es mehrere Ebenen der Korrektheit. In der Reihenfolge der zunehmenden Abstraktion:
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.
quelle
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).
quelle
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)
quelle