Niezawodne Systemy Informatyczne
Bombowiec B-1B (fot. avioners.net)
Formal methods should be part of the education of every computer scientist and software engineer, just as the appropriate branch of applied maths is a necessary part of the education of all other engineers.
Z raportu FAA (Federal Aviation Authority) i NASA
NVIDIA's selection of Ada and SPARK ushers in a new era in the history of safety- and security-critical software development.
Quentin Ochem
Aktualności
Opis kursu
Moduł wprowadzający w zagadnienia związane z tworzeniem systemów co do których stawia się krytyczne wymagania. Wykłady rozpoczynają się wstępem dotyczącym niezawodności oprogramowania i sprzętu. Następnie omawiane są narzędzia wspierające weryfikację poprawności programów stworzone dla języka Ada, który znajduje zastosowanie w systemach o znaczeniu krytycznym. Przedstawione zostaje również projektowanie kontraktowe wraz z przykładami jego wykorzystania w wybranych językach programowania. Podczas zajęć laboratoryjnych rozwiązywane są zadania dzięki którym nabywa się praktycznej umiejętności stosowania narzędzi omawianych na wykładach.
Zasady zaliczenia
- Ocena z grupy kursów wyliczana jest na podstawie średniej ważonej z punktów uzyskanych na laboratorium i na kartkówkach.
- Aby zaliczyć grupę kursów należy uzyskać co najmniej 50 punktów z laboratorium i co najmniej 50 punktów z kartkówek.
- Progi ocen zamieszczono poniżej w postaci kontraktu procedury Oblicz_Zaliczenie.
- Kartkówki odbędą się na ostatnich wykładach w semestrze (terminy i tematy zostaną podane wkrótce).
- Listy zadań na laboratoriach należy rozwiązywać samodzielnie i zaliczać w terminie podanym przy każdej liście.
- Laboratorium musi być zaliczone do 15-go tygodnia zajęć (nie ma możliwości zaliczania laboratorium w sesji egzaminacyjnej).
- Przy każdym zadaniu na liście zadań podano maksymalną liczbę punktów jaką można za nie otrzymać.
- Liczba punktów otrzymywanych za zadanie zależy od tego, które warunki udało się programowi gnatprove automatycznie udowodnić.
- Kryteria wyliczania liczby punktów podano na wstępie do każdej listy zadań.
procedure Oblicz_Zaliczenie(LAB: in Float; KOL: in Float; ZAL: out Float)
with
Pre =>
LAB >= 50.0 and LAB <= 100.0 and KOL >= 50.0 and KOL <= 100.0,
Contract_Cases =>
(
0.4*LAB + 0.6*KOL < 50.0 => ZAL = 2.0,
0.4*LAB + 0.6*KOL < 60.0 => ZAL = 3.0,
0.4*LAB + 0.6*KOL < 70.0 => ZAL = 3.5,
0.4*LAB + 0.6*KOL < 80.0 => ZAL = 4.0,
0.4*LAB + 0.6*KOL < 90.0 => ZAL = 4.5,
others => ZAL = 5.0
);
with
Pre =>
LAB >= 50.0 and LAB <= 100.0 and KOL >= 50.0 and KOL <= 100.0,
Contract_Cases =>
(
0.4*LAB + 0.6*KOL < 50.0 => ZAL = 2.0,
0.4*LAB + 0.6*KOL < 60.0 => ZAL = 3.0,
0.4*LAB + 0.6*KOL < 70.0 => ZAL = 3.5,
0.4*LAB + 0.6*KOL < 80.0 => ZAL = 4.0,
0.4*LAB + 0.6*KOL < 90.0 => ZAL = 4.5,
others => ZAL = 5.0
);
Plan wykładu
1 Wstęp (2h)
2-8 SPARK (14h)
- Elementy języka SPARK
- Pakiety
- Sprawdzanie zaleźności
- Dowodzenie
9-11 Metody formalne (6h)
- Weryfikacja programów while
- Analiza przepływu sterowania
- Analiza przepływu danych
12-13 Systemy krytyczne (4h)
- Pewność
- Specyfikowanie systemów krytycznych
- Wytwarzanie systemów krytycznych
14 Przegląd innych narzędzi (2h)
- Frama-C dla języka C
- KeY dla Java
15 Podsumowanie (2h)
Prezentacja
Wybrane przykłady prezentowane na wykładzie można znaleźć w repozytorium przemko/ada-spark-nsi.
Terminy kartkówek
Literatura
SPARK
- J. McCormick, P. Chapin. Building High Integrity Applications with SPARK. Cambridge University Press, 2015. (na stronie wydawcy w zakładce Resources znajdują się źródła programów zamieszczonych w książce)
- J. Barnes. SPARK: The Proven Approach to High Integrity Software. Altran Praxis, 2012. (dotyczy starszej wersji SPARK)
- SPARK 2014 Toolset User's Guide. AdaCore and Altran UK Ltd
- SPARK 2014 Reference Manual. AdaCore and Altran UK Ltd
- R. Chapman. Industrial Experience with SPARK. ACM SIGAda Ada Letters, Volume XX, Issue 4, Dec. 2000.
Metody formalne
- K.R. Apt, F.S. de Boer, E-R Olderog. Verification of Sequential and Concurrent Programs. Springer, 2009. (rozdziały 3 while Programs)
- J-F Bergeretti, B.A. Carre. Information-Flow and Data-Flow Analysis of while-Programs. ACM Transactions on Programming Languages and Systems, Vol. 7, No. 1, January 1985.
- P.B. Jackson, G.O. Passmore. Proving SPARK Verification Conditions with SMT solvers.
Niezawodność
- I. Sommerville. Inżynieria oprogramowania. WNT, Warszawa, 2003. (część IV Systemy krytyczne, część V Weryfikacja i zatwierdzanie)
- G.J. Holzmann. The Power of 10: Rules for Developing Safety-Critical Code. IEEE Computer, June 2006.
Dziesięć fundamentalnych zasad
Poniższe zasady pochodzą z artykułu Gerarda Holzmanna The Power of 10: Rules for Developing Safety-Critical Code:
- Ogranicz cały kod do bardzo prostych konstrukcji sterujących -- nie używaj instrukcji goto, konstrukcji setjmp lub longjmp ani bezpośredniej lub pośredniej rekurencji.
- W każdej pętli podawaj stały górny zakres.
- Nie używaj dynamicznej alokacji pamięci po zainicjowaniu.
- Żadna funkcja nie powinna być dłuższa na wydruku niż jedna strona papieru, przy standardowym formatowaniu z jednym wierszem na instrukcję i jednym wierszem na deklarację.
- Średnia liczba asercji w kodzie powinna wynosić co najmniej dwie na jedną funkcję.
- Deklaruj wszystkie obiekty danych w najwęższym możliwym zakresie.
- Każde wywołanie funkcji musi sprawdzać zwracaną przez funkcję wartość a każda wywołana funkcja musi sprawdzać poprawność wartości wszystkich parametrów dostarczonych w wywołaniu.
- Użycie preprocesora musi ograniczać się jedynie do dołączania plików nagłówkowych i prostych makrodefinicji.
- Użycie wskaźników musi być ograniczone. W szczególności poziom dereferencji nie może być większy niż jeden.
- Cały kod musi być kompilowany od pierwszego dnia rozwijania z ostrzeżeniami kompilatora na jak najbardziej pedantycznym poziomie. Cały kod musi kompilować się bez żadnych ostrzeżeń.
Odnośniki do stron
Przykłady projektów w języku SPARK
Poniższe projekty udostępniają źródła programów:
Narzędzia
Opis instalacji narzędzi w przypadku systemu Linux:
- Ściągnij plik gnat-2020-20200818-x86_64-linux-bin ze strony https://www.adacore.com/download/
- Ustaw flagę wykonywania dla ściągniętego pliku: chmod +x gnat-2020-20200818-x86_64-linux-bin
- Uruchom ściągnięty plik: ./gnat-2020-20200818-x86_64-linux-bin
- Nie zapomnij dopisać ścieżkę do katalogu GNAT/2020/bin w zmiennej środowiskowej PATH.
- Być może zaistnieje potrzeba doinstalowania pewnych pakietów (np. pod Debian 10 musiałem doinstalować pakiet libncurses5).
- Przeczytaj o ustawieniu zmiennej środowiskowej GPR_PROJECT_PATH w dokumentacji: https://docs.adacore.com/spark2014-docs/html/ug/en/install.html
Przykład weryfikacji
Załóżmy, że w pliku test.adb znajduje się następujący kod:
natomiast w pliku test.gpr następujący opis projektu:
Aby zweryfikować poprawność procedury Test, należy wydać następujące polecenie:
Pliki powstałe podczas weryfikacji znalazły się w podkatalogu gnatprove. Raport z weryfikacji został zamieszczony w pliku gnatprove/gnatprove.out:
Aby posprzątać po weryfikacji należy wykonać następujące polecenie:
procedure Test with SPARK_Mode is
X : Integer;
begin
X := 2 + 2;
pragma Assert (X = 4);
end Test;
natomiast w pliku test.gpr następujący opis projektu:
project Test is
for Main use ("test.adb");
end Test;
Aby zweryfikować poprawność procedury Test, należy wydać następujące polecenie:
$ gnatprove -P test.gpr
Phase 1 of 2: generation of Global contracts ...
Phase 2 of 2: flow analysis and proof ...
Summary logged in /Users/przemko/gnatprove/gnatprove.out
Pliki powstałe podczas weryfikacji znalazły się w podkatalogu gnatprove. Raport z weryfikacji został zamieszczony w pliku gnatprove/gnatprove.out:
$ more gnatprove/gnatprove.out
Summary of SPARK analysis
=========================
-------------------------------------------------------------------------------------------------------
SPARK Analysis results Total Flow Interval CodePeer Provers Justified Unproved
-------------------------------------------------------------------------------------------------------
Data Dependencies . . . . . . .
Flow Dependencies . . . . . . .
Initialization 1 1 . . . . .
Non-Aliasing . . . . . . .
Run-time Checks . . . . . . .
Assertions 1 . . . 1 (CVC4) . .
Functional Contracts . . . . . . .
LSP Verification . . . . . . .
-------------------------------------------------------------------------------------------------------
Total 2 1 (50%) . . 1 (50%) . .
Analyzed 1 unit
in unit test, 1 subprograms and packages out of 1 analyzed
Test at test.adb:3 flow analyzed (0 errors, 0 checks and 0 warnings) and proved (1 checks)
Aby posprzątać po weryfikacji należy wykonać następujące polecenie:
$ gnatprove -P test.gpr --clean
Listy zadań
Rozwiąż samodzielnie poniższe listy zadań:
Wskazówki
Czasami istnieje konieczność dopisania w programie asercji, które umożliwią automatyczne udowodnienie poprawności programu.
Na przykład, w zadaniu o schemacie Hornera można pomyśleć o takiej asercji:
pragma Assert (for all A in Integer => (for all B in Positive => A ** B = A * A ** (B - 1) ));
Na przykład, w zadaniu o schemacie Hornera można pomyśleć o takiej asercji:
pragma Assert (for all A in Integer => (for all B in Positive => A ** B = A * A ** (B - 1) ));