Verificación de Programas

Anuncio
Verificación de Programas
Parte II: Caracterización lógica de programas y contratos
Recap: El Traductor
Programa
Especificación
Traductor
Teorema en
una lógica
Demostrador
de Teoremas
Válido
Inválido
Verificador
Traduciendo un programa a un teorema...
●
●
●
●
Para esto, necesitamos representar a un programa como una fórmula de alguna lógica
Para ello, necesitamos darle semántica formal al programa
Una forma de hacerlo: usar semántica denotacional
Semántica denotacional: el significado del programa es un objeto matemático
Semántica denotacional
●
Supongamos un lenguaje de programación sencillo: ●
Asignación
x:=y
●
Skip
skip
●
Secuencia
P;Q
●
Condicional
if B then P else Q endif
●
Ciclo
while B do P endwhile
Ejemplo
y := x;
z := 1;
while y>1 do
z:=z*y;
y:=y­1
endwhile
●
Computa la función factorial de x con la salida en z
Triplas(*) de Hoare
●
Correctitud parcial: {A} s {B}
–
–
●
Si: ●
el programa comienza en un estado q cumple A
●
la ejecución de s termina
Entonces: el estado final cumple B
Correctitud total: [A] s [B]
–
Si: el programa inicia en un estado q cumple A
–
Entonces: ●
La ejecución termina
●
El estado final cumple B
(*) tripla no figura en el diccionario de la RAE
Reglas de Hoare
{A}s1{C} {C}s2{B} _________
{R}s1;s2{E} {P}skip{P} {A && G}s1{B} {A && !G}s2{B} {A} if G then s1 else s2 endif {B}
{A && G} s {A} (A && !G)=>B {A} while G do s endwhile {B}
Reglas de Hoare: asignación
Regla backward:
{ B[E/x] } x:= E {B}
Ejemplo:
{A} x:= x+2 {x>=5}
{x>=5[x+2/x]} x:=x+2 {x>=5}
{x+2>=5}x:=x+2{x>=5}
{x>=3}x:=x+2{x>=5}
Weakest precondition
Dijkstra: Verificar que {A} s {B}
a) Sea Pre(s,B) = {A'| {A'}s{B} }
b) < Pre(s,B) , => > es un reticulado, done
–
False \in Pre(s,B)
–
Si x,y \in Pre(s,B), ent. x && y, x || y \in Pre(s,B)
c) WP(s,B) = lub(R)
d) Computar WP(s,B) y demostrar si A=>WP(s,B)
Reticulado de “predicados”
Queremos verificar que:
{A} s {B}
S = [[true]]
WP(s,B)
Pre(s,B)
A
C \subseteq C' sii C'=>C
\empty = [[false]]
+ débil
(+estados)
+ fuerte
(­estados)
Weakest precondition
●
WP(skip, B) == B
●
WP(x:=E, B) == B[E/x]
●
WP( s1;s2 , B ) == WP(s1, WP(s2, B))
●
WP( if E then s1 else s2 endif, B ) == E=> WP(s1,B) && !E => WP(s2,B)
Ejemplo
returns c
requires true
ensures c= a || b
bool P(bool a, bool b) {
if (a)
c:=true
else
c:=b
}
WP(P, c=a||b) = ? Conjetura lógica a probar:
?
Ejemplo
returns c
requires true
ensures c= a || b
bool P(bool a, bool b) {
if (a)
c:=true
else
c:=b
}
WP(P, c=a||b) = a=> WP(c:=true,c=a||b) && !a => WP(c:=b,c=a||b) =
(a=> true=a||b) && !a => b=a||b)
Conjetura lógica a probar:
true=>(a=> true=a||b) && !a => b=a||b)
Weakest precondition: Ciclos
●
WP_k(while E do S endwhile, B) ==
WP_0(...) == !E => B
WP_i+1(...) == WP_i && E=>WP(S,WP_i(...))
●
WP(while E do S endwhile, B) ==
glb{WP_k | k>=0}
●
Calcular este glb de forma precisa es imposible en general
–
En algunos casos, igualmente es muy COSTOSO
Weakest precondition: Ciclos
●
Soluciones:
–
Loop unroll: “desenrollar” un loop una cantidad finita de veces
–
Anotar los ciclos con invariantes de ciclo
Teorema del Invariante
P => Inv
[B && Inv] S [Inv]
[B && Inv] S [v<\old(v)]
(Inv && v<=c) => !B
(!B && Inv) => Q [P] while B do S endwhile [Q]
Teorema del Invariante
P => Inv
{B && Inv} S {Inv}
(!B && Inv) => Q {P} while B do S endwhile {Q}
Tratamiento de ciclos
●
●
Extendemos el lenguaje que usamos:
–
havoc x
–
assert E
–
assume E
–
while(I,T) E do S enwhile Extendemos la definición de WP
–
WP(havoc x, B) == \forall x. B
–
WP(assume E, B) == E => B
–
WP(assert E, B) == E && B
Tratamiento de ciclos
●
Reescribimos los whiles de la siguiente forma:
while_(I,T) E do S endwhile ==
assert I; havoc T; assume I;
if (E) then S;assert I;assume false endif
–
I es el invariante de ciclo
–
T es el conjunto de variables modificadas en S
Intuición del invariante
assert I; havoc T; assume I;
if (E) then S;assert I;assume false endif
●
●
Para las ejecuciones sin entrar al ciclo, las simulamos directamente
Para las ejecuciones que entran al ciclo, hacemos una prueba de correctitud para múltiples iteraciones de ciclo
Condiciones de Verificación
●
Sea P un programa con ciclos,
P' la reescritura de P sin ciclos
●
Definimos VC(P,B) = WP(P',B) ●
VC(P,B) es una condición de verificación Condiciones de Verificación (2)
S = [[true]]
Queremos verificar que:
{A} s {B}
Problema: podemos rechazar
algunos programas/postcondiciones
que están bien
WP(s,B)
VC(s,B)
Pre(s,B)
A
C \subseteq C' sii C'=>C
\empty = [[false]]
+ débil
(+estados)
+ fuerte
(­estados)
Ejemplo
{???}
while_(x>=0,x) x>0 do x:=x­1 endwhile {x=0}
Ejemplo
{???}
assert x>=0; havoc x; assume x>=0;
if (x>0) then x:=x­1;
assert x>=0;
assume false endif
{x=0}
Ejemplo
{???}
L1 assert x>=0; L2 havoc x; L3 assume x>=0;
L4 if (x>0) then L5 x:=x­1;
L6 assert x>=0;
L7 assume false endif
{x=0}
WP(L1;L2;L3;L4, x=0)
WP(L1;L2;L3;L4, x=0) =
WP(L1, WP(L2, WP(L3, WP(L4, x=0)))) =
WP(L1, WP(L2, WP(L3, WP(L4, x=0)))) = ...
WP(L4,x=0)
WP(L4, x=0) =
WP(if x>0 then L5;L6;L7 else skip endif, x=0) = x>0 => WP(L5;L6;L7, x=0) && !x>0 => WP(skip, x=0) =
x>0 => WP(L5, WP(L6, WP(L7, x=0))) &&
!x>0 => x=0 = ...
WP L7, WP L6, WP L5
WP(L7, x=0) =
WP(assume false, x=0) =
false => x=0 =
true
WP(L6,true) =
WP(assert x>=0,true) =
x>=0 && true =
x>=0
WP(L5, x>=0) =
WP(x:=x­1, x>=0) =
x>=0[x­1/x] =
x­1>=0 =
x>=1
WP(L4,x=0) ­ Parte 2
WP(L4, x=0) =
WP(if x>0 then L5;L6;L7 else skip endif, x=0) = x>0 => WP(L5;L6;L7, x=0) && !x>0 => WP(skip, x=0) =
x>0 => WP(L5, WP(L6, WP(L7, x=0))) &&
!x>0 => x=0 = x>0 => x>=1 && x<=0 => x=0
WP(L1;L2;L3;L4, x=0) – Parte 2
WP(L1, WP(L2, WP(L3, (x>0 => x>=1)&&(x<=0 => x=0) ) )) =
WP(L1, WP(L2, WP(assume x>=0, (x>0 => x>=1)&&(x<=0 => x=0) ) )) =
WP(L1, WP(L2, x>=0 =>((x>0 => x>=1)&&(x<=0 => x=0)) )) =
WP(L1, WP(havoc x, x>=0 =>((x>0 => x>=1)&&(x<=0 => x=0)) )) =
WP(L1, forall x. (x>=0 =>((x>0 => x>=1)&&(x<=0 => x=0))) )) =
WP(assert x>=0, forall x. (x>=0 =>((x>0 => x>=1)&&(x<=0 => x=0))) )) =
WP(L1;L2;L3;L4, x=0) – Parte 3
WP(L1;L2;L3;L4, x=0) =
x>=0 && forall x.(x>=0 =>( (x>0 => x>=1)&&
(x<=0 => x=0) )) =
x>=0
Tratamiento de llamados a métodos
●
Soluciones
–
“inlining” del llamado
–
Usar el contrato del llamado
●
¿Cuál es más “fuerte”?
●
Reescribimos el llamado:
f() == assert pre; havoc M; assume post
Verificación de programas
1.Anotamos cada procedimiento con la precondición, las variables que modifica y la postcondición
2.Anotamos cada loop con el invariante y las variables que modifica
3.Ahora podemos analizar cada procedimiento modularmente (separadamente)
Verificación de programas (2)
Verificamos que:
requires pre
modifies M
ensures post
f() { S } 1.la fórmula VC(S',true) sea válida Sea S'==
assume pre;
S;
assert post
2.todas las modificaciones de S' estén contenidas en M
Algunos problemas
●
●
El tamaño de la fórmula VC puede ser exponencial en el tamaño del programa
Generación eficiente de la VC:
–
Usar más variables (usar SSA) –
Optimizar la estructura de la fórmula (colapsar ramas iguales, etc)
–
etc...
Algunos problemas (2)
●
Control flow no­estructurado: Excepciones, Gotos, etc.
●
Separar el programa en Bloques secuenciales ●
Usar variables para codificar el CFG
Recap
1.JML: Java Modeling Language
2.Triplas de Hoare
3.Capturar la validez de una tripla de Hoare usando uno fórmula lógica
●
WP: Weakest precondition
●
VC: Verification condition (+ fuerte que WP)
4.Falta: ¿cómo funciona un demostrador de teoremas ?
Fuentes de incompletitud/
unsoundness
●
Hasta ahora....
–
Escritura de invariantes de ciclo
●
Más débil : falso positivo
●
Más fuerte: no encuentra contraejemplo/modelos
–
Loop unrolling (si no hay invariantes de ciclo)
–
Contratos muy débiles
●
Subespecificar un procedimiento
Descargar