Co to jest bezpieczny język programowania?

55

Coraz popularniejsze stają się bezpieczne języki programowania (PL). Zastanawiam się, jaka jest formalna definicja bezpiecznej PL. Na przykład C nie jest bezpieczny, ale Java jest bezpieczna. Podejrzewam, że właściwość „bezpieczna” powinna być stosowana do implementacji PL, a nie do samej PL. Jeśli tak, omówmy definicję bezpiecznej implementacji PL. Moje własne próby sformalizowania tego pojęcia doprowadziły do ​​dziwnego wyniku, dlatego chciałbym usłyszeć inne opinie. Nie mów, że każde PL ma niebezpieczne polecenia. Zawsze możemy wziąć bezpieczny podzbiór.

beroal
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Gilles 'SO - przestań być zły'
„zawsze możemy wziąć bezpieczny podzbiór”. Jak możesz być pewien, że wynikowy język jest nadal kompletny w Turingu? (co zwykle rozumie się przez „język programowania”)
effeffe
„właściwość„ bezpieczna ”powinna być stosowana do implementacji PL, a nie do samej PL” - można nazwać PL safe, jeśli istnieje bezpieczna implementacja.
Dmitrij Grigoriew

Odpowiedzi:

17

Kiedy nazywamy język „bezpiecznym” pod pewnym względem , oznacza to formalnie, że istnieje dowód, że żaden dobrze sformułowany program w języku nie może zrobić czegoś, co uważamy za niebezpieczne. Słowo „bezpieczny” jest również używane mniej formalnie, ale to właśnie tutaj ludzie rozumieją twoje pytanie. Istnieje wiele różnych definicji właściwości, które chcemy mieć w „bezpiecznym” języku.

Kilka ważnych to:

  • Definicja „dźwięczności typu” Andrew Wrighta i Matthiasa Felleisena , przywoływana w wielu miejscach (w tym w Wikipedii) jako przyjęta definicja „bezpieczeństwa typu”, oraz ich dowód z 1994 r., Że podzbiór ML spełnia ten warunek.

  • Michael Hicks wymienia tutaj kilka definicji „bezpieczeństwa pamięci” . Niektóre są listami rodzajów błędów, które nie mogą wystąpić, a niektóre polegają na traktowaniu wskaźników jako możliwości. Java gwarantuje, że żaden z tych błędów nie jest możliwy (chyba że wyraźnie zaznaczysz funkcję oznaczoną unsafe), ponieważ moduł śmieciowy zarządza wszystkimi alokacjami i dezalokacjami. Rust daje taką samą gwarancję (ponownie, chyba że wyraźnie zaznaczysz kod jako unsafe), poprzez swój system typów afinicznych, który wymaga, aby zmienna była własnością lub była wypożyczona przed użyciem co najmniej raz.

  • Podobnie kod bezpieczny dla wątków jest zwykle definiowany jako kod, który nie może wykazywać pewnych rodzajów błędów dotyczących wątków i pamięci współdzielonej, w tym wyścigów danych i zakleszczeń. Te właściwości są często wymuszane na poziomie językowym: Rust gwarantuje, że wyścigi danych nie mogą wystąpić w systemie typów, C ++ gwarantuje, że std::shared_ptrinteligentne wskaźniki do tych samych obiektów w wielu wątkach nie usuną obiektu przedwcześnie lub nie skasują go podczas ostatniego odwołania aby go zniszczyć, C i C ++ mają dodatkowo atomiczmienne wbudowane w język, a operacje atomowe gwarantują wymuszenie pewnych rodzajów spójności pamięci, jeśli są używane poprawnie. MPI ogranicza komunikację międzyprocesową do jawnych komunikatów, a OpenMP ma składnię zapewniającą bezpieczny dostęp do zmiennych z różnych wątków.

  • Właściwość, której pamięć nigdy nie wycieknie, jest często nazywana „bezpieczną przestrzenią”. Automatyczne zbieranie śmieci jest jedną z funkcji językowych, które to zapewniają.

  • Wiele języków ma gwarancję, że jego działania będą miały dobrze określone wyniki, a programy będą dobrze zachowane. Jak supercat podał przykład powyższego, C robi to dla arytmetyki bez znaku (gwarantowane bezpieczne zawijanie), ale nie podpisanej arytmetyki (gdzie przepełnienie może powodować dowolne błędy, ponieważ C musiało obsługiwać procesory, które robią różne rzeczy po podpisaniu arytmetyki przepełnienia), ale wtedy język czasami cicho konwertuje niepodpisane ilości na podpisane.

  • Języki funkcjonalne mają dużą liczbę niezmienników, które każdy dobrze uformowany program gwarantuje, na przykład, że czyste funkcje nie mogą powodować skutków ubocznych. Mogą one, ale nie muszą być określane jako „bezpieczne”.

  • Niektóre języki, takie jak SPARK lub OCaml, zostały zaprojektowane w celu ułatwienia sprawdzenia poprawności programu. To może, ale nie musi być opisane jako „bezpieczne” od błędów.

  • Dowody na to, że system nie może naruszać formalnego modelu bezpieczeństwa (stąd powiedzenie: „Każdy system, który jest prawdopodobnie bezpieczny, prawdopodobnie nie jest”)

Davislor
źródło
1
To może, ale nie musi być opisane jako „bezpieczne” od błędów. Czy mógłbyś to trochę rozwinąć? Co rozumiesz przez „z błędów”?
scaaahu
2
@scaaahu Oto przykład strony internetowej, która formalnie udowodniła, że ​​oprogramowanie jest „poprawne pod względem bezpieczeństwa”. W tym kontekście odnosi się do oprogramowania, które zapobiega kolizjom samolotów, co oznacza, że ​​jest bezpieczne przed kolizjami.
Davislor
1
Przyjmuję tę odpowiedź, ponieważ zawiera ona listę rodzajów bezpieczeństwa. Miałem na myśli bezpieczeństwo pamięci.
beroal
Chociaż w tej odpowiedzi wymieniono kilka przydatnych linków i wiele przykładów, większość z nich jest całkowicie pomieszana. Czyszczenie pamięci zapewnia, że ​​pamięć nigdy nie wycieknie lub nieużywanie „niebezpiecznych” bloków automatycznie zapewni Ci bezpieczeństwo lub podpisane przepełnienie będące niezdefiniowanym zachowaniem, ponieważ kompilatory С muszą obsługiwać niektóre dziwne procesory, poważnie? I tylko krótkie słowo na temat Ada / SPARK, który jest jedynym z wymienionych języków, który poważnie traktuje bezpieczeństwo.
VTT
94

Nie ma formalnej definicji „bezpiecznego języka programowania”; to nieformalne pojęcie. Języki, które twierdzą, że zapewniają bezpieczeństwo, zwykle zawierają dokładne formalne oświadczenie o tym, jakiego rodzaju bezpieczeństwo jest żądane / gwarantowane / zapewniane. Na przykład język może zapewniać bezpieczeństwo typu, bezpieczeństwo pamięci lub inną podobną gwarancję.

DW
źródło
13
Addeumdum, jeśli mówimy o C vs Java jak post OP: to bezpieczeństwo pamięci jest gwarantowane w Javie, a nie w C. Bezpieczeństwo typu jest zapewniane przez oba na swój sposób. (Tak, wiele osób, które to czytają, już to wie, ale może niektórzy nie).
Walfrat
17
@Walfrat To część tego. Java również nie ma nieokreślonego zachowania, czego oczekujemy od języka, który nazywa się „bezpiecznym”. Jeśli chodzi o systemy typów, nie sądzę, że silny system typów statycznych jest tym, co ludzie mają na myśli przez określenie „bezpieczny”. Dynamicznie pisane języki, takie jak Python, są ogólnie „bezpieczne”.
Max Barraclough,
2
Moja definicja bezpieczeństwa typu to kontrola kompilacji, która sobie z tym poradzi. To może nie być formalna definicja. Zauważ, że powiedziałem „bezpieczeństwo typu”, a nie „bezpieczne”. Dla mnie „bezpieczny” język odnosi się do „mojej” definicji „bezpieczeństwa typu i pamięci” i myślę, że może być najbardziej rozpowszechniony. Oczywiście nie mówię o pewnym pułapce, takiej jak wskaźnik odbicia / pustki w C, którego kompilacja nie jest w stanie obsłużyć. Inną możliwą definicją bezpiecznego są programy, które nie powodują awarii z błędem segmentu, jak wskaźnik jednostkowy w C. Takie rzeczy są zwykle przyznawane w Pythonie i Javie.
Walfrat
7
@Walfrat Wszystko, co cię jednak przyciąga, to język, w którym składnia jest dobrze zdefiniowana. Nie gwarantuje, że wykonanie jest dobrze zdefiniowane - i ile razy widziałem awarię JRE, mogę powiedzieć, że jako system nie jest „bezpieczny”. Z drugiej strony w C MISRA włożyła wiele wysiłku w unikanie niezdefiniowanego zachowania, aby uzyskać bezpieczniejszy podzbiór języka, a kompilacja C w asemblerze jest znacznie lepiej zdefiniowana. To zależy od tego, co uważasz za „bezpieczne”.
Graham
5
@MaxBarraclough - „Java również nie ma niezdefiniowanego zachowania” - Java nie ma niezdefiniowanego zachowania w sensie stosowanym w specyfikacjach C w definicji języka (chociaż pozwala niektórym kodom na tworzenie wartości, które nie mają jednej wstępnie zdefiniowanej wartości, np. Dostęp zmienna, która jest modyfikowana w innym wątku lub poprzez dostęp do niej doublelub longpodczas, gdy jest modyfikowana w innym wątku, co nie gwarantuje, że nie zostanie wytworzona połowa jednej wartości zmieszana w jakiś nieokreślony sposób z połową innego), ale specyfikacja API jednak w niektórych przypadkach ma nieokreślone zachowanie.
Jules
42

Jeśli możesz dostać kopię Typów i języków programowania Benjamina Pierce'a , we wstępie ma dobry przegląd różnych perspektyw na temat „bezpiecznego języka”.

Jedną z proponowanych interpretacji tego terminu, która może Cię zainteresować, jest:

„Bezpieczny język jest całkowicie zdefiniowany w podręczniku programisty”. Niech definicja języka będzie zbiorem rzeczy, które programista musi zrozumieć, aby przewidzieć zachowanie każdego programu w tym języku. Zatem instrukcja dla języka takiego jak C nie stanowi definicji, ponieważ zachowanie niektórych programów (np. Tych, które obejmują niesprawdzony dostęp do tablicy lub arytmetykę wskaźników) nie może zostać przewidziane bez znajomości szczegółów, w jaki sposób dany kompilator C rozkłada struktury w pamięci itp., a ten sam program może mieć zupełnie inne zachowania, gdy jest wykonywany przez różne kompilatory.

Wahałbym się więc przed użyciem terminu „niebezpieczne” w odniesieniu do implementacji języka programowania. Jeśli niezdefiniowany termin w języku powoduje odmienne zachowanie w różnych implementacjach, jedna z implementacji może zapewnić zachowanie, które może być bardziej oczekiwane, ale nie nazwałbym tego „bezpiecznym”.

jaskółka oknówka
źródło
7
Problem zatrzymania mówi oczywiście, że bez względu na język, zawsze będą programy, których zachowanie nie jest przewidywalne na podstawie definicji języka. Tak więc każda definicja, która opiera się na „przewidywaniu zachowania każdego programu w języku” jest zasadniczo wadliwa dla dowolnego języka pełnego Turinga.
MSalters
15
@MSalters Jest to popularne nieporozumienie dotyczące problemu zatrzymania. Nierozstrzygalność problemu zatrzymania oznacza, że ​​niemożliwe jest mechaniczne ustalenie zachowania dowolnego programu w języku pełnym Turinga. Ale możliwe jest, że dla dowolnego danego programu, zachowanie jest przewidywalne. Po prostu nie można stworzyć programu komputerowego, który przewiduje taką prognozę.
Gilles „SO- przestań być zły”
7
@Giles: Tak nie jest. Załóżmy, że istnieje dowód braku wypowiedzenia dla każdego programu, który nie wygasa. Następnie możesz wymienić dowody braku wypowiedzenia, aby sprawdzić, czy dany program się zatrzymuje. Problem zatrzymania jest więc rozstrzygalny. Sprzeczność. W związku z tym niektóre programy, które nie kończą programu, prawdopodobnie nie są zakończone.
Kevin
9
@Gilles: Doskonale zdaję sobie sprawę z faktu, że wiele programów zostało w trywialny sposób zatrzymanych lub nie. Ale stwierdzenie tutaj dotyczy dosłownie zachowania każdego programu. Dowód twierdzenia Haltinga pokazuje, że istnieje co najmniej jeden program, dla którego nie jest to prawdą. To po prostu niekonstruktywny dowód, nie powie ci, który program jest nierozstrzygalny.
MSalters
8
@MSalters Myślę, że implikowany bit jest taki, że stwierdzenie dotyczy raczej zachowania programu na małą skalę niż zachowań wschodzących na dużą skalę. Weźmy na przykład hipotezę Collatza . Poszczególne etapy algorytmu są proste i dobrze zdefiniowane, ale zachowanie pojawiające się (ile iteracji do zatrzymania, a jeśli w ogóle tak się dzieje) jest niczym innym. - „Przewidywanie” jest tu używane nieformalnie. Można to lepiej napisać jako „wiedzieć, jak wykona się dowolna instrukcja w dowolnym programie”.
RM
18

Sejf nie jest binarny, to kontinuum .

Mówiąc nieformalnie, bezpieczeństwo oznacza sprzeciw wobec błędów, przy czym 2 najczęściej wymieniane to:

  • Bezpieczeństwo pamięci: język i jego implementacja zapobiegają różnym błędom związanym z pamięcią, takim jak użycie po użyciu, podwójny dostęp, dostęp poza granicami, ...
  • Bezpieczeństwo typu: język i jego implementacja zapobiegają licznym błędom związanym z typem, takim jak niesprawdzone rzutowania, ...

Nie są to jedyne klasy błędów, którym zapobiegają języki, wolność wyścigów danych lub swoboda impasu jest raczej pożądana, dowody poprawności są całkiem słodkie itp.

Po prostu niepoprawne programy rzadko są jednak uważane za „niebezpieczne” (tylko zawierają błędy), a termin „bezpieczeństwo” jest zasadniczo zarezerwowany dla gwarancji wpływających na naszą zdolność rozumowania programu. Zatem C, C ++ lub Go, posiadające niezdefiniowane zachowanie, są niebezpieczne.

Oczywiście istnieją języki z niebezpiecznymi podzbiorami (Java, Rust, ...), które celowo wyznaczają obszary, w których programista jest odpowiedzialny za utrzymanie gwarancji językowych, a kompilator jest w trybie „hands-off”. Języki nadal są ogólnie nazywane bezpiecznymi , pomimo tego luku ratunkowego, pragmatycznej definicji.

Matthieu M.
źródło
7
Powiedziałbym, że to krata.
PatJ
1
Większość implementacji języków programowania ma niebezpieczne funkcje (np. Obj.magicW Ocaml). A w praktyce są one naprawdę potrzebne
Basile Starynkevitch
4
@BasileStarynkevitch: Rzeczywiście. Sądzę, że każdy język z FFI koniecznie zawiera pewien poziom niebezpieczny, ponieważ wywołanie funkcji C będzie wymagało „piniowania” obiektów GC i ręcznego upewnienia się, że podpisy po obu stronach są zgodne.
Matthieu M.,
15

Chociaż nie zgadzam się z odpowiedzią DW, myślę, że pozostawia ona część „bezpiecznej” bez odpowiedzi.

Jak wspomniano, promowanych jest wiele rodzajów bezpieczeństwa. Uważam, że dobrze jest zrozumieć, dlaczego istnieje wiele pojęć. Każde pojęcie wiąże się z ideą, że programy cierpią szczególnie na pewną klasę błędów i że programiści nie byliby w stanie popełnić tego rodzaju błędu, gdyby język tego nie blokował.

Należy zauważyć, że te różne pojęcia mają różne klasy błędów i klasy te nie wykluczają się wzajemnie, ani nie obejmują one wszystkich form błędów. Na przykład 2 przykłady DW, pytanie, czy określona lokalizacja pamięci zawiera określony obiekt, jest zarówno kwestią bezpieczeństwa typu, jak i bezpieczeństwa pamięci.

Dalsza krytyka „bezpiecznych języków” wynika z obserwacji, że zakazanie niektórych konstrukcji uważanych za niebezpieczne pozostawia programistom potrzebę wymyślenia alternatyw. Empirycznie bezpieczeństwo jest lepiej osiągane dzięki dobrym bibliotekom. użycie kodu, który został już przetestowany w terenie, pozwala uniknąć nowych błędów.

MSalters
źródło
10
Ta witryna jest raczej nie na temat, ponieważ inżynieria oprogramowania nie jest tak naprawdę nauką, ale nie zgadzam się z twoim stwierdzeniem empirycznym. Korzystanie z dobrych bibliotek nie uratuje cię w niebezpiecznych językach, ponieważ nie jesteś chroniony przed niewłaściwym użyciem. Bezpieczne języki pozwalają uzyskać więcej gwarancji od autora biblioteki i uzyskać większą pewność, że używasz ich poprawnie.
Gilles „SO- przestań być zły”
3
W tej kwestii jestem z MSalters. - „Bezpieczne języki pozwalają uzyskać więcej gwarancji od autora biblioteki i uzyskać większą pewność, że używasz ich poprawnie”. Jest to non sequitur do wszystkich praktycznych celów.
Kapitan Giraffe,
9

Podstawowa różnica między C i Javą polega na tym, że jeśli uniknie się pewnych łatwych do zidentyfikowania cech Javy (np. W Unsafeprzestrzeni nazw), każda możliwa akcja, którą można podjąć - w tym „błędne” - będzie miała ograniczony zakres możliwych wyników . Wprawdzie ogranicza to to, co można zrobić w Javie - przynajmniej bez korzystania z Unsafeprzestrzeni nazw, ale umożliwia także ograniczenie szkód, które mogą być spowodowane przez błędny program lub - co ważniejsze - przez program, który poprawnie przetworzyłby prawidłowe pliki, ale nie są szczególnie chronione przed błędami.

Tradycyjnie kompilatory C przetwarzałyby wiele akcji w sposób zdefiniowany w standardzie w „normalnych” przypadkach, podczas gdy przetwarzałyby wiele przypadków narożnych „w sposób charakterystyczny dla środowiska”. Jeśli ktoś używałby procesora, który zwarłby się i zapaliłby, gdyby nastąpiło przepełnienie numeryczne, i chciałby uniknąć zapłonu procesora, musiałby napisać kod, aby uniknąć przepełnienia numerycznego. Gdyby jednak ktoś używał procesora, który idealnie szczęśliwie obcinałby wartości w uzupełnieniu do dwóch, nie trzeba unikać przepełnień w przypadkach, w których takie obcięcie skutkowałoby akceptowalnym zachowaniem.

Nowoczesne C idzie o krok dalej: nawet jeśli ktoś celuje w platformę, która w naturalny sposób zdefiniowałaby zachowanie czegoś takiego jak przepełnienie numeryczne, w którym Standard nie nakładałby żadnych wymagań, przepełnienie jednej części programu może wpłynąć na zachowanie innych części programować w sposób arbitralny, niezwiązany prawami czasu i przyczynowości. Rozważmy na przykład coś takiego:

 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.
 }

„Nowoczesny” kompilator języka C otrzymujący coś takiego jak powyższy może dojść do wniosku, że ponieważ obliczenia x * x przepełniłyby się, gdyby x był większy niż 46340, może bezwarunkowo wykonać wywołanie „foo”. Zauważ, że nawet jeśli akceptowalne byłoby nienormalne zakończenie działania programu, jeśli x jest poza zakresem, lub funkcja zwraca jakąkolwiek wartość w takich przypadkach, wywołanie foo () z poza zakresem x może spowodować szkody daleko poza jedna z tych możliwości. Tradycyjne C nie zapewniałoby żadnego sprzętu zabezpieczającego poza tym, co dostarczał programista i platforma, ale pozwoliłoby, aby sprzęt zabezpieczający ograniczył szkody w nieoczekiwanych sytuacjach. Nowoczesne C ominie każdy sprzęt bezpieczeństwa, który nie jest w 100% skuteczny w utrzymywaniu wszystkiego pod kontrolą.

supercat
źródło
3
@DavidThornley: Być może mój przykład był zbyt subtelny. Jeśli intma 32 bity, xzostanie awansowany do podpisanego int. Sądząc z uzasadnieniem, autorzy standardu oczekiwać, że nie dziwne implementacje potraktuje podpisane i niepodpisane typy w równoważny sposób poza niektórych szczególnych przypadkach, ale gcc czasami „optymalizuje” w taki sposób, że łamią jeżeli uint16_tprzez uint16_tmnożenie daje wynik poza INT_MAX , nawet jeśli wynik jest używany jako wartość bez znaku.
supercat
4
Dobry przykład. Jest to jeden z powodów, dla których powinniśmy zawsze (na GCC lub Clang) się kompilować -Wconversion.
Davislor,
2
@ Davislor: Ach, właśnie zauważyłem, że godbolt odwrócił kolejność, w jakiej są wymienione wersje kompilatora, więc wybranie ostatniej wersji gcc na liście daje raczej najnowszą niż najwcześniejszą. Nie sądzę, aby to ostrzeżenie było szczególnie pomocne, ponieważ jest podatne na oznaczanie wielu sytuacji, w return x+1;których nie powinno to być problematyczne, a przekazanie wyniku na uint32_t stłumiłoby komunikat bez rozwiązania problemu.
supercat
2
@supercat Eliminowanie testów nie ma sensu, jeśli kompilator musi umieścić testy z powrotem w innym miejscu.
user253751
3
@immibis: Dyrektywa „sprawdzone przy założeniu” może pozwolić kompilatorowi na zastąpienie wielu testów lub na kontrolę, która byłaby przeprowadzana wiele razy w pętli, z jednym testem, który można wyciągnąć poza pętlę. To lepsze niż wymaganie od programistów dodawania kontroli, które nie byłyby potrzebne w kodzie maszynowym, aby program spełniał wymagania, w celu zapewnienia, że ​​kompilator nie „zoptymalizuje” kontroli niezbędnych do spełnienia wymagań.
supercat
7

Istnieje kilka warstw poprawności w języku. W kolejności rosnącej abstrakcji:

  • Niewiele programów jest pozbawionych błędów (tylko te, dla których można udowodnić poprawność). Inni wspominali już, że ograniczenie błędów jest zatem najbardziej konkretnym aspektem bezpieczeństwa. Języki działające na maszynie wirtualnej, takie jak Java i .net, są na ogół bezpieczniejsze pod tym względem: błędy programu są zwykle przechwytywane i obsługiwane w określony sposób. 1
  • Na kolejnym poziomie błędy wykryte w czasie kompilacji zamiast w czasie wykonywania zwiększają bezpieczeństwo języka. Program poprawny pod względem składniowym powinien również być poprawny semantycznie w jak największym stopniu. Oczywiście kompilator nie zna dużego obrazu, więc dotyczy to poziomu szczegółowości. Silne i wyraziste typy danych są jednym z aspektów bezpieczeństwa na tym poziomie. Można powiedzieć, że język powinien utrudniać pewne rodzaje błędów(błędy typu, dostęp poza granicami, niezainicjowane zmienne itp.). Informacje o typie wykonawczym, takie jak tablice zawierające informacje o długości, pozwalają uniknąć błędów. Zaprogramowałem Ada 83 na studiach i odkryłem, że kompilujący program Ada zwykle zawierał może o rząd wielkości mniej błędów niż odpowiedni program C. Wystarczy wziąć pod uwagę zdolność Ady do definiowania przez użytkownika typów liczb całkowitych, których nie można przypisać bez wyraźnej konwersji: Całe statki kosmiczne uległy awarii, ponieważ mylono stopy i metry, których można było trywialnie uniknąć w przypadku Ady.

  • Na następnym poziomie język powinien zapewniać środki pozwalające uniknąć kodu typu „Booster”. Jeśli musisz napisać własne pojemniki, ich sortowanie, ich konkatenację, lub jeśli musisz napisać własny string::trim(), popełnisz błędy. Ponieważ poziom abstrakcji podnosi się, kryteria te dotyczą zarówno właściwego języka, jak i standardowej biblioteki języka.

  • Obecnie język powinien zapewniać środki do równoczesnego programowania na poziomie językowym. Współbieżność jest trudna do osiągnięcia i być może jest niemożliwa do prawidłowego wykonania bez obsługi języka.

  • Język powinien zapewniać środki do modularyzacji i współpracy. Silne, rozbudowane, zdefiniowane przez użytkownika typy powyżej pomagają tworzyć ekspresyjne interfejsy API.

Nieco ortogonalnie definicja języka powinna być zrozumiała; język i biblioteki powinny być dobrze udokumentowane. Zła lub brakująca dokumentacja prowadzi do złych i niewłaściwych programów.


1 Ponieważ jednak zwykle nie można udowodnić poprawności maszyny wirtualnej, takie języki mogą nieco paradoksalnie nie być odpowiednie dla bardzo surowych wymagań bezpieczeństwa.

Peter - Przywróć Monikę
źródło
1
+1 Dla jasnego wyjaśnienia warstwa po warstwie. Pytanie dla ciebie: Całe statki kosmiczne rozbiły się, ponieważ mylono stopy i metry, których można było trywialnie uniknąć w przypadku Ady. , czy mówisz o zgubieniu sondy Mars z powodu prostego błędu matematycznego ? Czy znasz język, którego używali dla tego statku kosmicznego?
scaaahu,
2
@ scaaahu Tak, myślę, że miałem na myśli to. Nie, nie znam języka. W rzeczywistości, czytając raport, wydaje się, że dane wysyłane przez sondę były przetwarzane przez oprogramowanie na Ziemi, które tworzyło plik danych, który był następnie wykorzystywany do określania poziomów ciągu. W tym scenariuszu proste pisanie w języku nie ma zastosowania. Przy okazji, mieli wiele problemów z naziemnym oprogramowaniem i formatem plików danych, co pomyłki uniemożliwiło wczesne wykrycie problemu. Tak więc historia nie jest bezpośrednim argumentem za mocnym pisaniem na maszynie, ale nadal jest przestrogą.
Peter - Przywróć Monikę
1

Nie mów, że każde PL ma niebezpieczne polecenia. Zawsze możemy wziąć bezpieczny podzbiór.

Każdy język, który znam, ma sposoby pisania nielegalnych programów, które można (kompilować i) uruchamiać. I każdy język, który znam, ma bezpieczny podzbiór. Więc jakie jest twoje pytanie?


Bezpieczeństwo jest wielowymiarowe i subiektywne.

Niektóre języki mają wiele operacji, które są „niebezpieczne”. Inni mają mniej takich operacji. W niektórych językach domyślny sposób robienia czegoś jest z natury niebezpieczny. W innych domyślny sposób jest bezpieczny. W niektórych językach istnieje wyraźny podzbiór „niebezpieczny”. W innych językach nie ma takiego podzbioru.

W niektórych językach „bezpieczeństwo” odnosi się wyłącznie do bezpieczeństwa pamięci - usługa oferowana przez standardową bibliotekę i / lub środowisko wykonawcze, w którym naruszenia dostępu do pamięci są utrudnione lub niemożliwe. W innych językach „bezpieczeństwo” wyraźnie obejmuje bezpieczeństwo wątków. W innych językach „bezpieczeństwo” oznacza gwarancję, że program się nie zawiesi (wymaganie to obejmuje niedopuszczanie nieprzechwyconych wyjątków). Wreszcie, w wielu językach „bezpieczeństwo” odnosi się do bezpieczeństwa typu - jeśli system typów jest pod pewnymi względami spójny, mówi się, że jest „dźwiękiem” (nawiasem mówiąc, Java i C # nie mają całkowicie dźwiękowych systemów typów).

W niektórych językach wszystkie różne znaczenia „bezpieczeństwa” są uważane za podzbiory bezpieczeństwa typu (np. Rust i Pony osiągają bezpieczeństwo gwintu dzięki właściwościom systemu typów).

Theodoros Chatzigiannakis
źródło
-1

Ta odpowiedź jest nieco szersza. Słowa „bezpieczeństwo i ochrona” zostały w ostatnich dziesięcioleciach okaleczone przez niektóre zorientowane politycznie części społeczeństwa anglojęzycznego, tak że ich powszechne użycie prawie nie ma definicji. Jednak w przypadku przedmiotów technicznych wciąż wracam do definiowania „bezpieczeństwa” i „bezpiecznego” jako: urządzenia, które zapobiega niezamierzonemu użyciu czegoś lub znacznie utrudnia przypadkowe użycie, a stan będący pod ochroną takiego urządzenia .
Tak więc bezpieczny język ma pewne urządzenie, które ogranicza konkretną klasę błędów. Oczywiście limity wiążą się w niektórych przypadkach z niedogodnościami, a nawet niemożnością, a to nie znaczy, że „niebezpieczne” języki będą powodować błędy. np. nie mam bezpiecznych korków na widelcach i od dziesięcioleci udało mi się, bez większego wysiłku, uniknąć zakleszczenia oka podczas jedzenia. Z pewnością mniej wysiłku niż przy użyciu korków. Bezpieczeństwo wiąże się z pewnymi kosztami, w stosunku do których należy je oceniać. (widelec korkowy jest odniesieniem do postaci Steve'a Martina)

Maksymalna moc
źródło