Verificación de Programas Orientados a Objetos j Análisis Automático de Código (2008) Spec# Francisco Bavera – Germán Regis 24/11/08 Presentación parcialmente basada en el tutorial de ETAPS 2008 de Leino y Monaham. Un Problema de Ingeniería de S ft Software Problema Desarrollar y mantener programas correctos. Un enfoque Especificaciones p registra g decisiones de diseño. Herramientas amplifican el esfuerzo del programador Manejan detalles Encuentran inconsistencias Aseguran g calidad 2 Spec# El Sistema de Programación Spec# provee un lenguaje y herramientas para verificar aserciones en programas orientados a objetos. El lenguaje de programación Spec#: extensión de C# con tipos non-null, contratos e invariantes de objetos. El compilador Spec#: compilador que verifica estáticamente tipos non-null y genera verificaciones en tiempo de ejecución para contratos e invariantes ifi d estático táti Spec#: S # este t componente t El verificador (Boogie) genera condiciones de verificación de programas Spec#. Internamente, usa un theorem prover automático t áti que analiza li lla condición di ió d de verificación para probar la correción del programa o encontrar errores. 3 Spec# El lenguaje Spec#: Lenguaje. NET Extensión de C#, Incluye: más tipos Especificaciones (pre (pre- y postcondiciones, postcondiciones etc etc.)) Verificación: Chequeos estáticos de tipos q p de ejecución j Chequeos en tiempo Verificación estática de programas 4 Instalando Spec# Bajar e instalar Spec# de http://research microsoft com/specsharp/ http://research.microsoft.com/specsharp/ Instalación incluye: el compilador, VS plug-in, Boogie, Z3 Requiere: .NET Recomendado: Visual Studio Opcional: Simplify 5 Como usamos Spec# El programador escribe cada clase con su especificación en un archivo fuente Spec# (similar a Eiffel o Java+JML) Entonces se puede ejecutar el verificador. El verificador ifi d se puede d iinvocar como ell compilador (de el IDE de Visual Basic o de la línea de comandos La interacción con el verificador se realiza modificando el archivo fuente. 6 El Lenguaje Spec# Spec# p es una extensión de C#, un lenguaje g j orientado a objetos para la plataforma f .NET C# incluye i l herencia h i simple i l (clases ( l pueden d implementar múltiples interfaces), referencias a objetos, invocación dinámica de métodos y j excepciones. Spec# incluye tipos non-null, non null contratos e invariantes. 7 Non--Null Tipos Non En C#, cada tipo T que es una referencia incluye el valor null Referencia a null (null dereference) es un error común en los programas. En Spec#, el tipo T! contiene solo referencias a null) objetos de tipo T (no contiene el valor null). int []! xs; declara un arreglo xs que no puede ser null. 8 Ejemplo de Tipo Non Non--Null public bli class l P Program { public static void Main(string[] args) { foreach (string arg in args) // Posible referencia null { Console.WriteLine(arg); // Posible referencia null } Console.ReadLine(); } } 9 Non--Null Tipos Non Spec# permite asignar la responsabilidad de asegurar que el parámetro sea non-null al i d d ét d invocador dell método. Spec# retorna Error: null is not a valid argument en caso de que un valor null se pase a un método q que requiera un p parámetro nonq null. 10 Ejemplo de Tipo Non Non--Null public bli class l P Program { public static void Main(string![] ! args) { foreach (string arg in args) { Console.WriteLine(arg); args != null } args[i] != null Console.ReadLine(); } } 11 Inicializando Atributos Non Non--Null class C : B { T ! x; public C(T ! y) { base(); this.x = y; this P(); this.P(); } Es type safe? No! abstract class B { public B() { this.M(); } public abstract int M(); } public override int M() p () { return x.f;; } } Referencia a null 12 Inicializando Atributos Non Non--Null class C : B { T ! x; Spec# permite que x S asignado Sea i d antes t d de invocar el constructor base. public C(T ! y) { this.x = y; base(); this P(); this.P(); } public override int M() p () { return x.f;; } } 13 Inicializando Atributos Non Non--Null using i Mi Microsoft.Contracts; ft C t t class C { T! x; [NotDelayed] public bli C(int C(i t k) { x = new T(k); b base(); () x.M(); } Permite que los atributos sean leidos Spec# permite llamadas base() en cualquier lugar En constructores E t t non-delayed, d l d ttodos d llos atributos t ib t non-null se deben inicializar antes de invocar base() 14 Assert public void [DoSomething(Node] node) { assert node != null; // verificado en run-time // normal processing continues } 15 Assert en C# public void [DoSomething(Node] node) { if ( node == null ) { throw [ApplicationException(...);] } else { // normal processing continues } } System.Diagnostics.Debug.Assert public void [DoSomething(Node] node) { System.Diagnostics.Debug.Assert(node !!= null); // normal processing continues } 16 Assume assume E es como assert E, en tiempo de ejecución, ejecución pero el verificador estático verifica el assert mientras que asume el assume. 17 Especificaciones de Métodos y Una especificación tiene tres partes: requires P; modifies x; ensures Q; y // precondición // frame condition // postcondición Una especificación es un contrato entre el invocador y la implementación: ◦ Invocador debe establecer P antes de invocar x Implementación puede asumir P en la entrada del método. ◦ Implementación debe establecer Q antes de retornar, y solo debe modificar x x Invocador puede asumir Q cuando retorna u que solo 18 fue modificado x. Especificaciones en .NET S i B ild A StringBuilder.Append dM Method h d (Ch (Char[[ ], ] Int32, I 32 Int32) I 32) Appends the string representation of a specified subarray of Unicode characters to the end of this instance. public StringBuilder Append(char[] value, int startIndex, int charCount); Parameters value A character array. startIndex The starting position in value. charCount The number of characters append. Return Value A reference to this instance after the append operation has occurred. Exceptions Exception Type Condition ArgumentNullException value is a null reference, and startIndex and charCount are not zero. ArgumentOutOfRangeException charCount is less than zero. -orstartIndex is less than zero. -orstartIndex + charCount is less than the length of value. 19 Precondiciones public StringBuilder Append(char[] value, int startIndex, int charCount ); requires value == null ==> startIndex == 0 && charCount == 0; requires 0 <= startIndex; requires 0 <= charCount; requires value == null || startIndex + charCount <= value.Length; Exception Type Condition ArgumentNullException value is a null reference, and startIndex and charCount are not zero. ArgumentOutOfRangeException charCount is less than zero. -orstartIndex is less than zero. -orstartIndex + charCount is less than the length of value. 20 Claúsulas Otherwise public StringBuilder Append( char[ ] value, int startIndex, int charCount ); requires q value != null ll startIndex == 0 && charCount == 0 otherwise ArgumentNullException; requires q 0 <= startIndex otherwise ArgumentOutOfRangeException; … Exception Type Condition ArgumentNullException g value is a null reference, and startIndex and charCount are not zero. ArgumentOutOfRangeException charCount is less than zero. -orstartIndex is less than zero zero. -orstartIndex + charCount is less than the length of value. 21 Claúsulas Throw void ReadToken (ArrayList a) throws EndOfFileException ensures a.Count ==old(a.Count); 22 Claúsula Modifies static void Swap(int[] a a, int i, i int j) requires 0 <= i && i < a.Length; requires q 0 <= j && j < a.Length; g modifies a[i], a[j]; ensures a[i] == old(a[j]); ensures a[j] == old(a[i]); { int temp; frame conditions limita que partes temp = a[i]; del estado del pprograma g ppueden ser a[i] = a[j]; j modificadas por el programa. a[j] = temp; } 23 Herencia y Contratos interface J { void M(int x); requires P; } class A { public abstract void M(int x); requires Q; } class B : A, A J{ public override void M(int x) {…} } Ciclos en Spec# p public static int SegSum(int[]! a, i int i, int j) requires 0 <= i && i <= j && j <= a.Length; ensures result == sum{int k in (i: j); a[k]}; { int s = 0;; for (int n = i; n < j; n++) { s += a[n]; } return s; } 25 Ciclos en Spec# p public static int SegSum(int[]! a, i int i, int j) requires 0 <= i && i <= j && j <= a.Length; ensures result == sum{int k in (i: j); a[k]}; { int s = 0;; for (int n = i; n < j; n++) { Cuando se trata de verificar s += a[n]; usando Spec#, se genera el } siguiente error: Array index return s; possibly below lower bound; el verificador necesitas más } información. 26 Invariantes de Ciclos public static int SegSum(int[]! a, int i, int j) requires i 0 <= i && i <= j && j <= a.Length; h ensures result == sum{int k in (i: j); a[k]}; { int i t s = 0; 0 for (int n = i; n < j; n++) invariant i <= n && n <= j; invariant s == sum{int k in (i: n); a[k]}; { s += a[n]; } return s; } 27 Invariantes de Ciclos public static int SegSum(int[]! a, int i, int j) requires i 0 <=ii && i <= j && j <= a.Length; h ensures result == sum{int k in (i:j); a[k]}; { int i t s = 0; 0 for (int n = i; n < j; n++) invariant i <= n && n <= j; invariant s == sum{int k in (i:n); a[k]}; { s += a[n]; Verifier Output: } Spec# Program Verifier return s; finished with 3 verified, } 0 errors 28 Funciones Variantes public static int SegSum(int[]! a, int i, int j) requires i 0 <= i && i <= j && j <= a.Length; h ensures result == sum{int k in (i: j); a[k]}; { int i t s = 0; 0 int i t n=i; i while (n < j) invariant i <= n && n <= j; invariant s == sum{int k in (i: n); a[k]}; invariant 0< 0<= j - n; { int vf = j - n; // función variante s += + a[n]; n++; assert j - n < vf; } return s; } Se puede S d usar assert para determinar información acerca de la función variante. 29 Arquitectura q del Verificador Spec# p Spec# Compilador Spec# MSIL (“bytecode”) ( bytecode ) verificador estático (Boogie) Traductor Motor Inferencia BoogiePL Generador VC C di ió verificación Condición ifi ió Probador teoremas correcto o lista de errores 30 BoogiePL Spec# C Java+JML Eiffel … Interprete abstracto Abstracción predicados BoogiePL Detector terminación … Simplify Z3 CVC 3 SMT Lib Coq … 31 BoogiePL y L Lenguaje j IIntermedio di y La semántica de Spec# es codificada en g BoogiePL y Puede ser usado pra otras actividades de verificación (como verificar otros lenguajes fuentes). fuentes) 32 Ejemplo: Traducción Spec# a BoogiePL class C : object { int x; C() { … } virtual int M(int n) { … } static void Main() { C c = new C(); c x = 12; c.x int y = c.M(5); } } 33 Ejemplo: Traducción Spec# a BoogiePL // class types const unique q System.Object: name; y j ; const unique C: name; class l C : object { axiom C <: System.Object; C <: System Object; function typeof(o: ref) returns (t: name); // fields type yp field; ; const unique C.x: <int>field; const unique allocated: <bool>field; int x; // the heap var Heap: [ref, <α>field]α; 34 Ejemplo: Traducción Spec# a BoogiePL Spec# if (Guard) S else T BoogiePL Then Branch assume Guard; S assume !Guard; T Else Branch 35 Ejemplo: Traducción Spec# a BoogiePL Spec# while (Guard(x)) (G ( )) invariant Inv(x) ( ) { S(x) S( ) } BoogiePL Loop Pre- assert Inv(x) ( ) d decessor Loop Head havoc x; assume Inv(x); Loop assume !Guard(x); Exit assume Guard(x); S(x) Loop Body assert Inv(x) 36 Condiciones de Verificación y Usa un lenguaje intermedio: BoogiePL y Probador de Teoremas Automático (Z3) ( ) ◦ Puede ocultarse al programador ◦ Genera contra-ejemplos y Probador de Teoremas Interactivo ◦ Requiere q “gurus” g 37 Chunker NextChunck Chunker.NextChunck public string! NextChunk() modifies this.*; g <= ChunkSize; ensures result.Length { string s; if (n + ChunkSize <= src.Length) { s = src.Substring(n, ChunkSize); } else { s = src.Substring(n); } n += s.Length; return s; } 38 Chunker.ChunkSize))) (IMPLIES (< 0 (select2 $Heap@5 this Chunker.ChunkSize)) (AND (LBLNEG |@28274| (<= 0 (select2 $Heap@5 this Chunker.n))) (IMPLIES (<= 0 (select2 $Heap@5 this Chunker.n)) (AND (LBLNEG |@28285| (<= (select2 $Heap@5 this Chunker.n) ($StringLen (select2 $Heap@5 this Chunker.src)))) (IMPLIES (<= (select2 $Heap@5 this Chunker.n) ($StringLength (select2 $Heap@5 this Chunker.src))) (LBLNEG |@28303| (FORALL ($p) (QID unknown.0:0) (SKOLEMID 55) (IMPLIES (AND (AND (NEQ $p nullObject) (EQ (select2 $Heap@5 $p $allocated) |@true|)) (AND (EQ (select2 $Heap@5 $p $ownerRef) this) (EQ (select2 $Heap@5 $p $ownerFrame) Chunker))) (AND (EQ (select $Heap@5 $p $inv) ($typeof $p)) (EQ (select2 $Heap@5 $p $localinv) ($typeof $p)))))) (IMPLIES (FORALL ($p) (QID unknown unknown.0:0) 0:0) (SKOLEMID (IMPLIES (AND (AND (NEQ $p nullObject) (EQ (select2 $Heap@5 $p $allocated) |@true|)) (AND (EQ (select2 $Heap@5 $p $ownerRef) this) ( (select2 $Heap@5 $p $ownerFrame) Chunker))) (AND (EQ (select2 $Heap@5 $p $inv) ($typeof $p)) (EQ (select2 $Heap@5 $p $localinv) ($typ $p))))) (IMPLIES (EQ $Heap@6 (store2 $Heap@5 this $localinv ($typeof this))) (IMPLIES (EQ (IsHeap $Heap@6) |@true|) (IMPLIES TRUE block4607-2-block4590_correct)))))))))))))))))))) (FORMULA true4471to4607_correct (IMPLIES (LBLPOS |+5569| TRUE) (IMPLIES TRUE (IMPL (EQ nullObject nullObject) (IMPLIES TRUE block4607_correct))))) (FORMULA true4505to4607_correct (IMPLIES (LBLPOS |+5733| TRUE) (IMPLIES TRUE (IMPLIES (NEQ ($A ($As nullObject llObj t Mi Microsoft.Contracts.ICheckedException) ft C t t ICh k dE ti ) nullObject) llObj t) (IMPLIES TRUE bl block4607_correct))))) k4607 t))))) (FORMULA block4556-2-block4590_correct (IMPLIES (LBLPOS |+25363| TRUE) (IMPLIES (EQ $Heap@7 $Heap@5) (IMPLIES (EQ stack0s@ stack0s) (IMPLIES (EQ stack0o@4 nullObject) block4590_correct))))) (FORMULA block4556_correct (IMPLIES (LBLPOS |+5737| TRUE) (IMPL TRUE (IMPLIES TRUE block4556-2-block4590_correct)))) (FORMULA false4505to4556_correct (IMPLIES (LBLPOS |+5735| TRUE) (IMPLIES TRUE (IMPLIES (EQ ($As nullObject Microsoft.Contracts.ICheckedException) nullObject) (IMPLIES TRUE block4556_correct))))) (FORMULA block4505_correct _ ((IMPLIES ((LBLPOS ||+5575|| TRUE)) (IMPLIES ( TRUE (IMPLIES ( TRUE (AND ( true4505to4607_correct _ false4505to4556_correct))))) (FORMULA false4471to4505_correct (IMPLIES (LBLPOS |+5571| TRUE) (IMPLIES TRUE (IMPLIES (NEQ nullOb nullObject) (IMPLIES TRUE block4505_correct))))) (FORMULA block4471_correct (IMPLIES (LBLPOS |+5554| TRUE) (IMPLIES TRUE (IMPLIE TRUE (AND true4471to4607_correct false4471to4505_correct))))) (FORMULA block4284_correct (IMPLIES (LBLPOS |+5401| TRUE) (IMPLIES TRUE (AND (LBLNEG |@27875| (NEQ this nullObject)) (IMPLIES (NEQ this nullObject) (IMPLIES (EQ stack0i@2 (select2 $Heap@4 this Chunker.n)) (AND (LBLNEG |@27893| (NEQ s@0 nullObject)) (IMPLIES (NEQ s@0 nullObject) (IMPLIES (EQ stack1i@5 ($StringLength s@0) (IMPLIES (EQ stack0i@3 (+ stack0i@2 stack1i@5)) (AND (LBLNEG |@27916| (NEQ this nullObject)) (IMPLIES (NEQ this nullObject) (AND (LBLNEG |@27923| (OR (EQ (select2 $Heap@4 this $ownerFrame) $PeerGroupPlaceholder) (OR (NOT (EQ (<: (select2 $Heap@4 (select2 $Heap@4 this $ownerRef) $inv) (select2 $Heap@4 this $ownerFrame)) |@true|)) (EQ (select2 $Heap@4 (select2 $Heap@4 this $ownerRef) $localinv) ($BaseClass (select2 $Heap@4 this $ownerFrame)))))) (IMPLIES (OR (EQ (select2 $Heap@4 this $ownerFrame) $PeerGroupPlaceholder) (OR (NOT (EQ (<: (select2 $Heap@4 (select2 $Heap@4 this $ownerRef) $inv) (select2 $Heap@4 this $ownerFrame) |@true|)) (EQ (select2 $Heap@4 (select2 $Heap@4 this $ownerRef) $localinv) ($BaseClass (select2 $Heap@4 this $ownerFrame))))) (IMPLIES ( Q $Heap@5 (EQ $ @ (store2 ( $ $Heap@4 @ this Chunker.n C stack0i@3)) @ )) ((AND ((LBLNEG G |@28007| |@ | (OR (O (NOT ( O (AND ( (EQ ( Q (<: ( (select2 ( $ $Heap@5 @ this $inv) $ ) Chunker) |@true|) (NEQ (select2 $Heap@5 this $localinv) ($BaseClass Chunker)))) (< 0 (select2 $Heap@5 this Chunker.ChunkSize)))) (IMPLIE (OR (NOT (AND (EQ (<: (select2 $Heap@5 this $inv) Chunker) |@true|) (NEQ (select2 $Heap@5 this $localinv) ($BaseClass Chunker)))) (< 0 (select2 $Heap@5 this Chunker.ChunkSize))) (AND (LBLNEG |@28055| (OR (NOT (AND (EQ (<: (select2 $Heap@5 this $inv) Chunker) |@true (NEQ (select2 $Heap@5 this $localinv) ($BaseClass Chunker)))) (<= 0 (select2 $Heap@5 this Chunker.n)))) (IMPLIES (OR (NOT (AND (EQ (< ((select2 $ $Heap@5 p@ this $ $inv)) Chunker)) |@ |@true|) |) (NEQ ( Q (select2 ( $Heap@5 $ p@ this $ $localinv)) ($ ($BaseClass Chunker)))) )))) ((<= 0 ((select2 $ $Heap@5 p@ this Chunker.n))) (AND (LBLNEG |@28103| (OR (NOT (AND (EQ (<: (select2 $Heap@5 this $inv) Chunker) |@true|) (NEQ (select2 $Heap@5 this $localinv) ($BaseClass Chunker)))) (<= (select2 $Heap@5 this Chunker.n) ($StringLength (select2 $Heap@5 this Chunker.src))))) (IMPLIES (O (NOT (AND $Heap@5 this Chunker.n) ($StringLength (select2 $Heap@5 this Chunker.src)))) (IMPLIES (EQ (IsHeap $Heap@5) |@true|) (IMPL TRUE block4471_correct))))))))))))))))))))))) (FORMULA block4267-2-block4284_correct (IMPLIES (LBLPOS |+25361| TRUE) (IMPLIES (EQ stack2i@1 stack2i) (IMPLIES (EQ s@0 call4133formal@$result@0) (IMPLIES (EQ stack1i@4 stack1i@3) (IMPLIES (EQ $ActivityIndicator@2 $ActivityIndicator@1) (IMPLIES (EQ stack0o@2 stack0o@1) (IMPLIES (EQ $Heap@4 $Heap@3) block4284 block4284_correct)))))))) correct)))))))) (FORMULA block4267_correct (IMPLIES (LBLPOS |+2908| TRUE) (IMPLIES TRUE (AND (LBLNEG |@26919| (NEQ this nullObject)) (IMPLIES (NEQ this nullObject) (IMPLIES (EQ stack0o@1 (select2 $Heap@1 this Chunker.src)) (AND (LBLNEG |@26937| (NEQ this nullObject)) (IMPLIES (NEQ th nullObject) (IMPLIES (EQ stack1i@3 (select2 $Heap@1 this Chunker.n)) (AND (LBLNEG |@26955| (NEQ stack0o@1 nullObject)) (IMPLIES (N stack0o@1 nullObject) (IMPLIES (AND (EQ ($IsNotNull call4133formal@$result System.String) |@true|) (EQ (select2 $Heap call4133formal@$result $allocated) |@true|)) (AND (LBLNEG |@26962| (<= 0 stack1i@3)) (IMPLIES (<= 0 stack1i@3) (AND (LBLNEG |@2696 Condiciones de Verificación: Chunker.NextChunck Otra Herramienta y y y y y ESC/Java E f Enfoque similar i il a SSpec# # Usa el mismo probador Simplify Usa el lenguaje de especificación JML Actualmente no cuenta con un GUI estable? 40 Resumiendo y Spec# yExtiende C# con contratos. contratos yPuede ser usado para verificación estática y dinámica. yNo es completo. completo yEficiente y Práctico. yEn desarrollo. yExiste herramientas similares (ESC/Java) 41 Referencias y Recursos y y y Spec# website http://research.microsoft.com/specsharp/ The Spec# programming system: An overview. Mike Barnett, K. Rustan M. Leino, and Wolfram Schulte. In CASSIS 2004,, LNCS vol. 3362, Springer, 2004. Boogie: g A Modular Reusable Verifier for Objectj Oriented Programs. Mike Barnett, Bor-Yuh Evan Chang, Robert DeLine, Bart Jacobs, and K. Rustan M. Leino. In FMCO 2005, LNCS vol. 4111, Springer, 2006. 42