Preuve et vérification des systèmes logiciels
Preuve et vérification des systèmes logiciels
Master CILS
2018–19
I Commandes d’avions
Systèmes auto-* ?
I Redondance matérielle
I Model checking
I Preuve
I Analyse statique
I Méthodes formelles pendant la conception (raffinement)
Sur un système qui peut évoluer, on veut être sûr que le comportement reste
celui prévu
I Production de traces
I Monitoring
Tests
Spécifications
de validation
Conception
Tests d'intégration
Architecturale
Codage
Master CILS: Preuve, analyse statique et vérification runtime, 10
Méthodes formelles : Pour qui ?
I Hardware (Intel)
Spécifications
Exemple
La fonction racine carrée sur les entiers
/* @ requires a est un entier positif
* ensures l ’ entier retourne est la racine carree de a */
int root ( int a ) {
int i = 0;
int k = 1;
int sum = 1;
while ( sum <= a ) {
k = k +2;
i = i +1;
sum = sum + k ;
}
return i ;
}
Master CILS: Preuve, analyse statique et vérification runtime, 18
Spécifications
Formellement
Langage de spécification ACSL
/* @ requires a >= 0;
* ensures \ result *\ result <= a && (\ result +1)*(\ result +1) > a ; *
int root ( int a ) {
int i = 0;
int k = 1;
int sum = 1;
while ( sum <= a ) {
k = k +2;
i = i +1;
sum = sum + k ;
}
return i ;
}
Master CILS: Preuve, analyse statique et vérification runtime, 19
Spécifications
Exercice
Plus d’expressivité
Indice maximum dans un tableau
/* @ requires sizeof ( a ) > 0;
ensures
\ forall int i ;
(0 <= i && i < sizeof ( a ))
== > a [ i ] <= a [\ result ]; */
int indice_max ( int a []) {
int i ;
int r = 0;
for ( i = 1; i < sizeof ( a ); i ++)
if ( a [ i ] > a [ r ]) r = i ;
return r ;
}
Exercices
Tri
int [] sort ( int a []);
/* tri du tableau a */
Tri
int [] sort ( int a []);
/* tri du tableau a */
Tableau résultant trié :
ensures \ forall int i , j ;
0 <= i < j < sizeof ( a ) == > \ result [ i ] <= result [ j ];
Tri
int [] sort ( int a []);
/* tri du tableau a */
Tableau résultant trié :
ensures \ forall int i , j ;
0 <= i < j < sizeof ( a ) == > \ result [ i ] <= result [ j ];
Tableau résultant permutation du tableau initial :
ensures sizeof (\ result ) == \ sizeof ( a ) &&
\ forall int i ; 0 <= i < sizeof ( a ) == >
\ exists int j ; 0 <= j < sizeof ( a ) &&
a [ i ] == \ result [ j ];
Est-ce assez ?
Master CILS: Preuve, analyse statique et vérification runtime, 26
Spécifications
Tri (cont.)
Tri en place
On vérifie ensuite que les postconditions (ensures) sont bien impliquées par les
préconditions (requires) via l’exécution du programme.
Logique de Hoare
I Affectation x = e;
I Séquence { i1 ... in }
Exemple :
∀z. x ≤ z ⇒ y + 2 = z
Triplet de Hoare
Exemple
{x = n ∧ n > 0} y = 1; while (x != 1) { y = y * x; x = x - 1; }
{y = n! ∧ n > 0}
Ici, n est une variable logique, elle permet de se référer à la valeur de x au début
du programme
I {x = y}x = x + 3;{x = y + 3}
F1 F2 F4
F3 F5
F6
F0
I 1 règle par construction du langage + 2 règles logiques
Skip
Séquence
Affectation
aff
{[e/x]P}x = e;{P}
Rappel : [e/x]P est la formule P où toutes les occurrences libres de x ont été
substituées par e, par exemple [x + 1/x](x > 1) vaut x + 1 > 1
I L’affectation change l’état, il faut donc que la logique de Hoare reflète ce
changement
I De droite à gauche :
Pour que P soit vrai après l’exécution, il fallait que, pour un x remplacé par
sa nouvelle valeur, il soit vrai avant
Exemple : {x + 1 > 5}x = x + 1;{x > 5}
Substitutions
[y/x]x =
Substitutions
[y/x]x = y
Substitutions
[y/x]x = y
[y/x]z =
Substitutions
[y/x]x = y
[y/x]z = z
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y =
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
[y/x](∀z, z mod 2 = 0 ⇒ x ∗ x = z) =
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
[y/x](∀z, z mod 2 = 0 ⇒ x ∗ x = z) =
∀z, z mod 2 = 0 ⇒ y ∗ y = z
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
[y/x](∀z, z mod 2 = 0 ⇒ x ∗ x = z) =
∀z, z mod 2 = 0 ⇒ y ∗ y = z
[y/x](∀y, x mod 2 = 0 ⇒ x ∗ y = z) =
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
[y/x](∀z, z mod 2 = 0 ⇒ x ∗ x = z) =
∀z, z mod 2 = 0 ⇒ y ∗ y = z
[y/x](∀y, x mod 2 = 0 ⇒ x ∗ y = z) =
renommage du y lié pour éviter la capture de y
[y/x](∀t, x mod 2 = 0 ⇒ x ∗ t = z) =
Substitutions
[y/x]x = y
[y/x]z = z
[y/x]x > y = y > y
[y/x](∀x, x mod 2 = 0 ⇒ x ∗ y = z) =
∀x, x mod 2 = 0 ⇒ x ∗ y = z
[y/x](∀z, z mod 2 = 0 ⇒ x ∗ x = z) =
∀z, z mod 2 = 0 ⇒ y ∗ y = z
[y/x](∀y, x mod 2 = 0 ⇒ x ∗ y = z) =
renommage du y lié pour éviter la capture de y
[y/x](∀t, x mod 2 = 0 ⇒ x ∗ t = z) =
(∀t, y mod 2 = 0 ⇒ y ∗ t = z)
Master CILS: Preuve, analyse statique et vérification runtime, 53
Logique de Hoare, Weakest Precondition
Exemple
Conditionnelle
{P ∧ b}c1{Q} {P ∧ ¬b}c2{Q}
if
{P}if (b) c1 else c2{Q}
Utilisation
Prenons {x > 2}if (x>2) y = 1; else y = -1;{y > 0}
En utilisant la règle if, il faut donc montrer
{x > 2 ∧ x > 2}y = 1;{y > 0} et
{x > 2 ∧ ¬x > 2}y = -1;{y > 0}
ce que l’on peut simplifier en
{x > 2}y = 1;{y > 0} et
{⊥}y = -1;{y > 0}
Or à partir de la règle aff, on ne peut dériver que :
{1 > 0}y = 1;{y > 0} et
{−1 > 0}y = -1;{y > 0}
Les règles structurelles ne suffisent pas, il faut utiliser des règles logiques
Master CILS: Preuve, analyse statique et vérification runtime, 56
Logique de Hoare, Weakest Precondition
Conséquence gauche
Une condition P est dite plus forte qu’une condition Q si P ⇒ Q est valide
logiquement
Similairement, Q est dite plus faible que P.
P ⇒ P0 {P 0 }c{Q}
consG
{P}c{Q}
Il est correct de rendre une précondition plus spécifique (plus forte). Cette règle
permet de renforcer les préconditions.
Exemple :
x=4⇒x>2 {x > 2}x = x + 1;{x > 3}
consG
{x = 4}x = x + 1;{x > 3}
Conséquence droite
Boucle : intuition
I il faut vérifier
Boucle
{I ∧ b}c{I }
while
{I }while (b) c{I ∧ ¬b}
Exercice
Exercice
Soit c le programme :
{ i = 0;
s = 0;
while ( i != n ) {
i = i + 1;
s = s + 2 * i - 1; } }
Vérifier que s = i ∗ i est un invariant de la boucle.
Montrer que {>}c{s = n ∗ n} est valide (la somme des n premiers nombres
impairs est égale à n 2 ).
Exercice
1. Montrer que pour tout P et tout c, le triplet de Hoare {P}c{>} est valide.
Automatisme
Non-déterminisme
Non-déterminisme
Étant donné un triplet de Hoare, pour essayer de montrer sa validité, on a
plusieurs choix :
I Appliquer la règle correspondant à la syntaxe du programme
• Comment trouver la propriété intermédiaire pour la séquence ?
{P}c1 {Q} {Q}c2 {R}
seq
{P}{c1 c2 }{R}
• Comment trouver l’invariant pour les boucles ?
{I ∧ b}c{I }
while
{I }while (b) c{I ∧ ¬b}
I Appliquer une règle de conséquence
Non-déterminisme
Étant donné un triplet de Hoare, pour essayer de montrer sa validité, on a
plusieurs choix :
I Appliquer la règle correspondant à la syntaxe du programme
I Appliquer une règle de conséquence
• Gauche ou droite ?
• Comment trouver la bonne précondition plus forte, resp. la bonne
postcondition plus faible ?
P ⇒ P0 {P 0 }c{Q}
consG
{P}c{Q}
• Comment prouver P ⇒ P ? 0
Calcul de WP
Exercice
Calculer :
I WP({x = x + 1; y = y - 1;}, x > y)
!
if (x > y) x = x - y;
I WP , (x > 0 ∧ y > 0)
else y = y - x;
Cas de la boucle
WP(while (b) c, Q) = pas de formule simple !
Condition de vérification
Calcul de VC
I VC(;, Q) = Q
I VC(x = a;, Q) = [a/x]Q
I VC({c1 c2}, Q) = VC(c1, VC(c2, Q))
I VC(if (b) c1 else c2, Q) = (b ⇒ VC(c1, Q)) ∧ (¬b ⇒ VC(c2, Q))
IVC(while (b) c //@invariant I , Q) =
I ∧ Preserv ∧ Final (l’invariant est vérifié en début de boucle)
où
Preserv = ∀x1 , . . . , xm . I ∧ b ⇒ VC(c, I )
(l’invariant est préservé par une itération)
Final = ∀x1 , . . . , xm . ¬b ∧ I ⇒ Q
(la postcondition est satisfaite lorsque la boucle se termine)
x1 . . . xm sont les variables modifiées dans c
Master CILS: Preuve, analyse statique et vérification runtime, 74
Logique de Hoare, Weakest Precondition
Exemple
On reprend la factorielle :
{x = n ∧ n > 0}{ y = 1; while (x != 1) { y = y * x; x = x - 1; } }
{y = n! ∧ n > 0}
On prend comme invariant Inv = (y ∗ x! = n! ∧ x > 0 ∧ n > 0)
On a VC(fact, y = n! ∧ n > 0) =
1 ∗ x! = n! ∧ x > 0 ∧ n > 0
∧ ∀xy. (Inv ∧ x 6= 1) ⇒ (y ∗ x ∗ (x − 1)! = n! ∧ x − 1 > 0 ∧ n > 0)
∧ ∀xy. (x = 1 ∧ Inv) ⇒ (y = n! ∧ n > 0)
On peut “facilement” montrer que x = n ∧ n > 0 ⇒ VC(fact, y = n! ∧ n > 0)
Théorème
Si P ⇒ VC(c, Q) alors {P}c{Q} est valide.
Preuve : condG appliqué au théorème précédent.
Grâce aux programmes annotés et à VC, on obtient une méthode réaliste pour
faire de la preuve de programmes impératifs :
I Écrire un programme contenant les invariants des boucles et éventuellement
des assertions à certains point du programme difficiles pour la preuve ;
I Transmettre ce code annoté à un outil qui engendre les conditions de
vérification (exemple : plugin WP de Frama-C, cf. TP) ;
I Prouver ces conditions de vérification, de préférence avec un prouveur
automatique (alt-ergo, Z3, Vampire, . . .) ou interactif (Coq, Isabelle, . . .)
Frama-C et WP
Frama-C
I A framework for modular analysis of C code
I Frama-C : plateforme développé par le CEA et INRIA pour la vérification de
programmes C (analyse statique, preuve déductive . . . )
I http://frama-c.com, distribué sous LGPL (Fluorine v3 en juin 2013)
I Noyau construit sur CIL (Necula et al., Berkeley)
I ACSL langage d’annotations
I Plateforme extensible :
• Collaboration d’analyseurs sur un même code
• Communication entre les plugins via les annotations ACSL
• Ajout facile de nouveaux plugins
Value Analysis
Jessie WP
Aoraï
Agen
Specification Generation
Abstract Interpretation Deductive Verification Mthread
Concurrency
Formal Methods
Executable-ACSL
Spare code
Semantic constant folding SANTE
Browsing of unfamiliar code
Slicing
Impact Analysis
Annotations en ACSL
requires introduit une pré-condition
requires n>=0;
\result représente le résultat de la fonction
ensures introduit une post-condition
ensures \result>=0;
assigns précise les (locations mémoire des) variables
que la fonction a le droit de modifier
en dehors de ses variables locales
assigns \nothing ;
loop invariant invariant de boucle
loop variant variant
loop assigns spécifie les variables modifiées par la boucle
Behaviors (cont.)
Interface graphique
Interface textuelle
Un exemple : getMin
Spec informelle : Calculer le plus petit élément d’un tableau non vide
/* @ r e qu i r e s \ valid ( t +(0.. n -1)) && n > 0;
ensures \ forall integer i ; 0 <= i < n == > \ result <= t [ i ];
*/
int getMin ( int t [] , int n ) {
int res = t [0];
/* @ loop i n v a r i a n t 1 <= i <= n &&
(\ forall integer j ; 0 <= j < i == > res <= t [ j ]);
loop assigns res ;
loop variant n - i ;
*/
for ( int i = 1; i < n ; i ++)
if ( t [ i ] < res ) res = t [ i ];
return res ;
}
WP : OK
WP : OK
Master CILS: Preuve, analyse statique et vérification runtime, 91
Frama-C et WP
ACSL utilise des entiers mathématiques (integer) alors qu’à l’exécution C utilise
des entiers machine (int)
On peut utiliser les entiers bornés si besoin.
On peut aussi vérifier les overflows (RTE - runtime error).