• Aucun résultat trouvé

Invariants inductifs pour l’atteignabilité arrière

6.3 Production de certificats

6.3.1 Invariants inductifs pour l’atteignabilité arrière

Pour Cubicle, ce certificat n’a pas besoin de contenir toutes les étapes de calcul faites par le model checker comme les pré-images, les tests de point fixe ou de sûreté. La notion de sûreté d’un système est fortement liée à celle d’invariance. L’analyse de sûreté revient même à s’assurer qu’une propriété est un invariant du système. Pour un système donné, l’ensemble des états atteignables constitue l’invariant inductif le plus fort (partie verte de la figure figure 6.1(a)). De manière duale, l’ensemble des états ne pouvant atteindre un

mauvais état du système constitue l’invariant inductif le plus faible du système (partie verte de la figure 6.1(b)).

I

P���∗(I )

Θ

(a) Invariant inductif le plus fort

I

P��∗(Θ) Θ

(b) Invariant inductif le plus faible

Figure6.1 – Invariants inductifs calculés par des analyses d’atteignabilité avant et arrière L’ensemble V calculé par Cubicle dans l’algorithme 4 est en réalité la négation de cet invariant inductif le plus faible. Il constitue en lui même une preuve ou un certificat de la sûreté du système. En effet il est très simple de voir si une formule ϕ est un invariant inductif d’un système S = (Q,I,τ ). Il suffit pour cela qu’elle vérifie les deux conditions suivantes :

I (X ) |= ϕ (X ) (6.1)

initialisation ϕ (X ) ∧ τ (X ,X) |= ϕ (X). (6.2) préservation Le cas de base (6.1) dit que l’invariant ϕ doit être vrai dans les états initiaux du système et le cas inductif (6.2) dit que l’invariant doit être préservé par la relation de transition. Si en plus on a

ϕ (X ) |= P (X ) (6.3)

propriété alors la propriété P est un invariant du système.

Si on prend ϕ = ¬V et P = ¬Θ, où V est la disjonction des éléments visités de l’algorithme 4 et Θ est la formule dangereuse du système, alors ces trois conditions sont vé-rifiées. En effet on a V |= Pre

τ(Θ), V est close par pré-image, i.e. V (X) ∧ τ (X ,X) |= V (X ) donc ¬V (X ) ∧τ (X ,X

6.3 Production de certificats

Pour dire si le résultat de l’algorithme 4 implémenté par Cubicle est correct, il suffit alors de s’assurer que ϕ = ¬V et P = ¬Θ satisfont les trois conditions (6.1), (6.2) et (6.3). Dans le cadre des systèmes à tableaux, I , τ , V et Θ sont des formules du premier ordre. On pourrait donc prouver ces conditions avec un assistant de preuve ou à l’aide d’un démonstrateur automatique si on souhaite effectuer la certification sans intervention humaine. Dans ce dernier cas il faut faire confiance au solveur qu’on choisit. C’est pour cette raison qu’on a décidé d’utiliser Why3, on peut de cette façon accroître notre niveau de confiance lorsque différents prouveurs confirment de manière indépendante que les résultats sont corrects.

Exemple. Cubicle construit l’ensemble V suivant pour l’algorithme du mutex de la sec-tion 2.2.1 :

V = { ∃z1z2.z1 ,z2∧State[z1] =Crit∧State[z2] =Crit,

∃z1z2.z1 ,z2∧Turn = z2∧State[z1] =Crit∧State[z2] =Want, ∃z1z2.z1 ,z2∧Turn = z2∧State[z1] =Crit∧State[z2] =Idle}

Il génère ensuite un certificat sous la forme d’un ficher Why3 contenant quatre théories : Mutex_defs, Mutex_initialisation, Mutex_property et Mutex_preservation. La première permet de factoriser les définitions de symboles et de types.

theory Mutex_defs

type proc

type state = Idle | Want | Crit

function turn : proc

function turn’ : proc

function state proc : state

function state’ proc : state

end

Cette théorie déclare le type énuméré state et les symboles de fonction turn et state ainsi que leur version « prime ». Le type proc de Cubicle est interprété par les entiers mathématiques de Why3 (int) lorsque cela est nécessaire (en présence d’inégalités) ou par un type abstrait proc sinon . La théorie Mutex_initialisation contient les obligations de preuve correspondant à la condition (6.1).

theory Mutex_initialisation

use import Mutex_defs

axiom initial:

forall z:proc. state z = Idle

goal invariant_1:

not (exists z1 z2:proc. z1 <> z2 /\ state z1 = Crit /\ state z2 = Crit)

goal invariant_2:

not (exists z1 z2:proc. z1 <> z2 /\

turn = z2 /\ state z1 = Crit /\ state z2 = Want)

goal invariant_3:

not (exists z1 z2:proc. z1 <> z2 /\

turn = z2 /\ state z1 = Crit /\ state z2 = Idle)

end

Onse place dans un état initial donc on suppose la formule initiale (axiome initial) et on demande à vérifier que chaque invariant est valide.

La théorie Mutex_property contient les obligations de preuve correspondant à la condi-tion (6.3), c’est à dire qu’elle demande de vérifier que l’invariant inductif implique la propriété désirée.

theory Mutex_property

use import Mutex_defs

axiom invariant_1:

not (exists z1 z2:proc. z1 <> z2 /\ state z1 = Crit /\ state z2 = Crit)

axiom invariant_2:

not (exists z1 z2:proc. z1 <> z2 /\

turn = z2 /\ state z1 = Crit /\ state z2 = Want)

axiom invariant_3:

not (exists z1 z2:proc. z1 <> z2 /\

turn = z2 /\ state z1 = Crit /\ state z2 = Idle)

goal property_1:

not (exists z1 z2:proc. z1 <> z2 /\ state z1 = Crit /\ state z2 = Crit)

end

6.3 Production de certificats

dangereuses. Ici property_1 correspond exactement à l’axiome invariant_1.

Enfin la théorie Why3 Mutex_preservation contient les obligations de preuve à dé-charger pour montrer que l’invariant est inductif, c’est-à-dire qu’il est préservé par la relation de transition.

theory Mutex_preservation

use import Mutex_defs

axiom induction_hypothesis_1:

not (exists z1 z2:proc. z1 <> z2 /\ state z1 = Crit /\

state z2 = Crit)

axiom induction_hypothesis_2:

not (exists z1 z2:proc. z1 <> z2 /\ turn = z2 /\

state z1 = Crit /\ state z2 = Want)

axiom induction_hypothesis_3:

not (exists z1 z2:proc. z1 <> z2 /\ turn = z2 /\ state z1 = Crit /\ state z2 = Idle) axiom transition_relation: (* transition req *) (exists i:proc. (* requires *) state i = Idle /\ (* actions *) turn’ = turn /\ forall _j1:proc.

if _j1 = i then state’ _j1 = Want

else state’ _j1 = state _j1) \/

(* transition enter *)

(exists i:proc.

(* requires *)

turn = i /\ state i = Want /\

(* actions *)

turn’ = turn /\ forall _j2:proc.

if _j2 = i then state’ _j2 = Crit

else state’ _j2 = state _j2) \/ (* transition exit *) (exists i:proc. (* requires *) state i = Crit /\ (* actions *) forall _j3:proc.

if _j3 = i then state’ _j3 = Idle

else state’ _j3 = state _j3)

goal invariant_1:

not (exists z1 z2:proc. z1 <> z2 /\ state’ z1 = Crit /\

state’ z2 = Crit)

goal invariant_2:

not (exists z1 z2:proc. z1 <> z2 /\ turn’ = z2 /\

state’ z1 = Crit /\ state’ z2 = Want)

goal invariant_3:

not (exists z1 z2:proc. z1 <> z2 /\ turn’ = z2 /\

state’ z1 = Crit /\ state’ z2 = Idle)

end

On suppose que l’invariant inductif est vrai initialement (par les axiomes induction_ hypothesis_i) et on exprime la relation de transition sous la forme d’une formule reliant les symboles turn et state aux symboles turn’ et state’. Cette formule est l’expression

directe de τ (X ,X) dans le langage logique de Why3. On demande finalement à vérifier que les invariants sur les versions prime des symboles sont toujours vrais.

L’exemple jouet du mutex est trivial et tous les prouveurs automatiques sont capables de vérifier le certificat fourni par Cubicle. En revanche, si on prend l’exemple du protocole de cohérence de cache German-esque de la section 2.2.4, l’invariant inductif est composé de 17 clauses universellement quantifiées. On rapporte les temps de vérification obtenus pour différents démonstrateurs automatiques lancés au travers de la plateforme Why3 en figure 6.2. Chaque prouveur a été lancé avec un timeout de cinq secondes. Les temps sont donnés en secondes et les cases vertes correspondent à une réponse positive (valid), les cases rouges correspondent à une réponse non concluante (unknown). Enfin les cases oranges dénotent les exécutions qui n’ont pas abouti dans le temps imparti (T.O.).

On peut remarquer que les obligations de preuve correspondant à la condition d’ini-tialisation (6.1) et à la propriété (6.3) sont aisément déchargées par la totalité des huit prouveurs quasiment instantanément. En revanche les obligations concernant la préser-vation de l’invariant sont plus difficiles. Certains prouveurs ne terminent pas à temps alors que d’autres n’arrivent pas à conclure. Ces performances plutôt décevantes peuvent être imputées aux quantificateurs (universels et existentiels) omniprésents dans ces buts et en particulier dans la représentation logique de la relation de transition. La plupart des solveurs utilisent des heuristiques sensibles pour traiter ces quantificateurs donc les performances sont parfois inégales et rarement prévisibles. Mêmes si les résultats ne sont pas idéaux, ils sont largement suffisants car toutes les obligations sont déchargées par au moins quatre prouveurs.