600 likes | 781 Views
Software Model-checking: The SAnToS/Bandera Perspective. SAnToS Laboratory, Kansas State University, USA. http://www.cis.ksu.edu/bandera. Principal Investigators. Postdocs and Students. Radu Iosif Hongjun Zheng Corina Pasareanu Georg Jung. Robby Venkatesh Ranganath Oksana Tkachuk
E N D
Software Model-checking:The SAnToS/Bandera Perspective SAnToS Laboratory, Kansas State University, USA http://www.cis.ksu.edu/bandera Principal Investigators Postdocs and Students Radu Iosif Hongjun Zheng Corina Pasareanu Georg Jung Robby Venkatesh Ranganath Oksana Tkachuk William Deng Matt Dwyer John Hatcliff Support US National Science Foundation (NSF) US National Aeronautics and Space Agency (NASA) US Department of Defense Advanced Research Projects Agency (DARPA) US Army Research Office (ARO) Rockwell-Collins ATC Honeywell Technology Center and NASA Langley Sun Microsystems Intel
The Bandera Perspective This talk will focus on Bandera and Cadena and will give the Bandera/SAnToS perspective on software model-checking • Java PathFinder – JPF (NASA Ames) • SLAM Project (Microsoft Research) • BLAST Project (U. Berkeley) • FeaVer Project (Lucent/Bell Labs) • Alloy (MIT) For other perspectives see…
Goals of the Project I.Provide platform for construction of and experimentation with technologies for model-checking concurrent Java software II. Integration with commonly used design notations, methods,and processes III.Evaluation using safety-critical military and civilian applications as well as non-critical popular open-source software … model-reduction techniquese.g., abstraction, slicing, compiler-based optimizations … model-checking enginese.g., explicit-state, symbolic … property specification languagese.g., temp logic, state machines … UML artifacts, CCMe.g., checking, specification … integration with developmentand certification of safety-criticalsystems. … automatic generation of synchronization code with dedicated checking
In This Talk… • Challenges in model-checking software and how Bandera addresses these (30 minutes) • Overview of Bandera tool architecture and functionality of primary components (40 minutes) • --- break --- • Specification Patterns (20 minutes) • Modeling Avionics Software (40 minutes) • Conclusions (10 minutes)
Goals • Draw connections with earlier lectures and explain how various concepts and techniques are similar/different in software • Highlight hard open problems related to software model-checking • Share what I think are future trends in software model-checking and why we as a community have some reasons for being optimistic
Model Checking Error trace Line 5: … Line 12: … Line 15:… Line 21:… Line 25:… Line 27:… … Line 41:… Line 47:… OK Finite-state model or Model Checker (F W) Temporal logic formula
What makes model-checking software difficult? Line 5: … Line 12: … Line 15:… Line 21:… OK or Finite-state model Error trace Model Checker (F W) Temporal logic formula Problems using existing checkers: • State explosion • Model Construction • Property specification • Output interpretation
Model Construction Problem Gap Semantic gap: Programming Languages Model Description Languages void add(Object o) { buffer[head] = o; head = (head+1)%size; } Object take() { … tail=(tail+1)%size; return buffer[tail]; } Model Checker Program Model Description methods, inheritance, dynamic creation, exceptions, etc. automata
Model Construction Problem Unit • Due to state explosion, model-checking should not be applied to an entire code base, but rather to a unit • In OO software, boundaries between units are usually messy! • references flow out of unit, and external components can change state of objects created in unit • call-backs (in all GUI code) • tedious to identify interaction points and define stubs/drivers Code Base
What makes model-checking software difficult? Line 5: … Line 12: … Line 15:… Line 21:… OK or Finite-state model Error trace Model Checker (F W) Temporal logic formula Problems using existing checkers: • State explosion • Model Construction • Property specification • Output interpretation
Property Specification Problem Difficult to formalize a requirement in temporal logic “Between the window open and the window close, button X can be pushed at most twice.” …is rendered in LTL as... []((open /\ <>close) -> ((!pushX /\ !close) U (close \/ ((pushX /\ !close) U (close \/ ((!pushX /\ !close) U (close \/ ((pushX /\ !close) U (close \/ (!pushX U close))))))))))
Property Specification Problem Heap.b.head == Heap.b.tail Forced to state property in terms of model rather than source We want to write source level specifications... We are forced to write modellevel specifications... (((_collect(heap_b) == 1)\ && (BoundedBuffer_col.instance[_index(heap _b)].head == BoundedBuffer_col.instance[_index(heap _b)].tail) )\ || ((_collect(heap _b) == 3)\ && (BoundedBuffer_col_0.instance[_index(heap _b)].head == BoundedBuffer_col_0.instance[_index(heap _b)].tail) )\ || ((_collect(heap _b) == 0) && TRAP))
Property Specification Problem Variables b1 b2 b3 Heap object Complications arise due to the dynamic nature of OO software Consider multiple instances of a bounded buffer class... If a buffer instance becomes full, it will eventually become non-full. Requirement: In general, a heap object has no program-level name that persists throughout the lifetime of the object.
What makes model-checking software difficult? Line 5: … Line 12: … Line 15:… Line 21:… OK or Finite-state model Error trace Model Checker (F W) Temporal logic formula Problems using existing checkers: • State explosion • Model Construction • Property specification • Output interpretation
State Explosion Problem • Moore’s law and algorithm advances can help • Holzmann: 7 days (1980) ==> 7 seconds (2000) • Explosive state growth in software limits scalability blah, blah, blah …
What makes model-checking software difficult? Line 5: … Line 12: … Line 15:… Line 21:… OK or Finite-state model Error trace Model Checker (F W) Temporal logic formula Problems using existing checkers: • State explosion • Model Construction • Property specification • Output interpretation
Output Interpretation Problem Gap Error trace Mapping to source is made difficult by • Semantic gap & clever encodings of complex features • multiple optimizations and transformations Line 5: … Line 12: … Line 15:… Line 21:… Line 25:… Line 27:… … Line 41:… Line 47:… void add(Object o) { buffer[head] = o; head = (head+1)%size; } Object take() { … tail=(tail+1)%size; return buffer[tail]; } Model Description Program Raw error trace may be 1000’s of steps long Must map line listing onto model description • Over-approximations in abstractions may yield infeasible error traces (how to decide if feasible or not?)
Bandera:An open tool set for model-checking Java source code Graphical User Interface Optimization Control Checker Inputs Transformation & Abstraction Tools Checker Outputs Error Trace Mapping Bandera Temporal Specification Model Checkers void add(Object o) { buffer[head] = o; head = (head+1)%size; } Object take() { … tail=(tail+1)%size; return buffer[tail]; } Java Source Bandera
Addressing theModel Construction Problem void add(Object o) { buffer[head] = o; head = (head+1)%size; } Object take() { … tail=(tail+1)%size; return buffer[tail]; } Static Analyses Abstract Interpretation Optimizations Slicing Java Source Model Compiler Model Description Model extraction: compiling to model checker inputs: • Numerous analyses, optimizations,two intermediate languages, multiple back-ends • Slicing, abstract interpretation, specialization • Variety of usage modes: simple...highly tuned
Addressing theModel Construction Problem Unit Bandera Environment Generation Tools • Automatically finds points of interaction (where unit calls outside classes or is called itself) • Identify classes in unit Code Base
Addressing theModel Construction Problem Java encoding of state-machine Driver Unit Stubs Bandera Environment Generation Tools • Automatically finds points of interaction (where unit calls outside classes or is called itself) • Identify classes in unit • Cuts away non-unit classes • Automatically generates driver (generates calls to unit based on regular expression or LTL formula) Closed Unit Code Base • Automatically generates stubs
Addressing theProperty Specification Problem Using the pattern system: 2-bounded existence Between {open} and {close} {pushX} exists atMost {2}times; An extensible language based on field-tested temporal property specification patterns []((open /\ <>close) -> ((!pushX /\ !close) U (close \/ ((pushX /\ !close) U (close \/ ((!pushX /\ !close) U (close \/ ((pushX /\ !close) U (close \/ (!pushX U close))))))))))
Addressing theState Explosion Problem Property Generate models customized wrt property! void add(Object o) { buffer[head] = o; head = (head+1)%size; } … Java Source Model Compiler Model Descriptions • Result: multiple models • even as many as one per property • Aggressive customization via slicing, abstract interpretation, program specialization
Addressing theOutput Interpretation Problem + simulator Like a debugger: error traces mapped back to source Model Description void add(Object o) { buffer[head] = o; head = (head+1)%size; } Object take() { … tail=(tail+1)%size; return buffer[tail]; } Intermediate Representations Model Checker Model Compiler Java Source Line 5: … Line 12: … Line 15:… Lin e 21:… Error trace • Run error traces forwards and backwards • Program state queried • Heap structures navigated & visualized • Locks, wait sets, blocked sets displayed
Bandera Architecture Translators Property Tool Abstraction Engine BIR Analyses BIRC SPIN HSF-SPIN dSPIN Java Jimple Parser SMV Slicer Simulator JPF Error Trace Display
Bounded Buffer Initialization head tail Add,Add tail head Add,Take,Take tail head class BoundedBuffer { Object [] buffer; int head; /* next available slot */ int tail; /* last available slot */ int bound; /* max # of elements */ public BoundedBuffer(int b) {…} public synchronized boolean isEmpty() {…} public synchronized void add(Object o) {…} public synchronized Object take () {…} }
Property Specification Bandera Specification: FullToNonFull: {Full(b)} leads to {!Full(b)} globally; /** * @observable * EXP Full: (head == tail); */ Requirement: If a buffer becomes full, it will eventually become non-full. class BoundedBuffer { Object [] buffer; int head, tail, bound; public synchronized void add(Object o) {…} public synchronized Object take () {…} } forall[b:BoundedBuffer].
Property Specification Requirement: Bandera Specification: NoTakeWhileEmpty: {take.Return(b)} is absent after {Empty(b)} until {add.Call(b)}; /** * @observable * EXP Empty: * head == ((tail+1) % bound); */ Empty buffers must added to before being taken from class BoundedBuffer { int head, tail, bound; public synchronized void add(Object o) {…} public synchronized Object take () {…} } /** * @observable INVOKE Call; */ /** * @observable RETURN Return; */ forall[b:BoundedBuffer].
Quantification forall[b:BoundedBuffer].P(b) • Quantified set BoundedBuffer is not fixed • varies within executions • varies across executions • Solution • add a state variable (for b) that will eventually be bound non-deterministically to each instance • by enabling checking of the formula only when variable is bound to an instance
Quantification (Cont’d) 1 new BoundedBuffer(n) new BoundedBuffer(n) 1 1 1 selected selected selected 2 new BoundedBuffer(m) new BoundedBuffer(m) new BoundedBuffer(m) 2 2 selected selected new BoundedBuffer(k) 3 new BoundedBuffer(k) new BoundedBuffer(k) new BoundedBuffer(k) 3 selected (!selected (!selected U (selected && P(b))) || []!selected (selected && P(b))) []!selected Original Model Augmented Model !selected !selected !selected !selected
Quantification (Cont’d) Original Model Augmented Model class heap { public static BoundedBuffer b; } class BoundedBuffer { Object [] buffer; int head, tail, bound; public BoundedBuffer(int n) { ... if (heap.b == null && Bandera.choose()) { heap.b = this; } } } class BoundedBuffer { Object [] buffer; int head, tail, bound; public BoundedBuffer(int n) { ... } }
Quantification (Cont’d) Bandera compiles to… (heap.b == null U (heap.b != null && ([](heap.b.head == heap.b.tail) -> <>(heap.b.head != heap.b.tail)))) || [](heap.b == null) forall[b:BoundedBuffer]. {Full(b)} leads to {!Full(b)} globally;
Front End public synchronized void add(java.lang.Object) { T$0 := @this; o := @parameter0; entermonitor T$0; label0: goto label4; label1: virtualinvoke T$0.[wait():void](); T$3 = T$0.[head:int]; T$4 = T$0.[buffer:Object[]]; T$4[T$3] = o; Jimple (excerpts) public synchronized void add(Object o) { while ( tail == head ) try { wait(); } catch (InterruptedException ex) {} buffer[head] = o; head = (head+1) % bound; notifyAll(); } Java
Property-directed Slicing indirectly relevant Slice mentioned in property Resulting slice • slicing criterion generated automatically from observables mentioned in the property Source program • backwards slicing automatically finds all components that might influence the observables
Property-directed Slicing Slicing Criterion All statements that assign to head, tail. removed by slicing Included in slicing critirion indirectly relevant /** * @observable EXP Full: (head == tail) */ class BoundedBuffer { Object [] buffer_; int bound; int head, tail; public synchronized void add(Object o) { while ( tail == head ) try { wait(); } catch ( InterruptedException ex) {} buffer_[head] = o; head = (head+1) % bound; notifyAll(); } ... }
Abstraction Engine int (n<0) : neg (n==0): zero (n>0) : pos Signs Signs x = zero; if (x == zero) x = pos; neg zero pos Collapses data domains via abstract interpretation: Code Data domains int x = 0; if (x == 0) x = x + 1;
Abstraction Component Functionality PVS BASL Compiler Bandera Abstraction Specification Language Jimple Abstracted Jimple Abstraction Engine Jimple Concrete Type Abstract Type Inferred Type Variable x int Signs y int Signs Signs done bool Bool Abstraction Library count int intAbs …. …. o Object Point b Buffer Buffer
Specification Creation Tools Automatic Generation Forall n1,n2: neg?(n1) and neg?(n2) implies not pos?(n1+n2) Proof obligations submitted to PVS... Forall n1,n2: neg?(n1) and neg?(n2) implies not zero?(n1+n2) Forall n1,n2: neg?(n1) and neg?(n2) implies not neg?(n1+n2) abstraction Signs abstracts int begin TOKENS = { NEG, ZERO, POS }; abstract(n) begin n < 0 -> {NEG}; n == 0 -> {ZERO}; n > 0 -> {POS}; end operator + add begin (NEG , NEG) -> {NEG} ; (NEG , ZERO) -> {NEG} ; (ZERO, NEG) -> {NEG} ; (ZERO, ZERO) -> {ZERO} ; (ZERO, POS) -> {POS} ; (POS , ZERO) -> {POS} ; (POS , POS) -> {POS} ; (_,_)-> {NEG, ZERO, POS}; end Example: Start safe, then refine:+(NEG,NEG)={NEG,ZERO,POS}
Compiling In Abstractions Compiled abstraction Signs abstracts int begin TOKENS = { NEG, ZERO, POS }; abstract(n) begin n < 0 -> {NEG}; n == 0 -> {ZERO}; n > 0 -> {POS}; end operator + add begin (NEG , NEG) -> {NEG} ; (NEG , ZERO) -> {NEG} ; (ZERO, NEG) -> {NEG} ; (ZERO, ZERO) -> {ZERO} ; (ZERO, POS) -> {POS} ; (POS , ZERO) -> {POS} ; (POS , POS) -> {POS} ; (_,_)-> {NEG, ZERO, POS}; /* case (POS,NEG), (NEG,POS) */ end public class Signs { public static final int NEG = 0; // mask 1 public static final int ZERO = 1; // mask 2 public static final int POS = 2; // mask 4 public static int abstract(int n) { if (n < 0) return NEG; if (n == 0) return ZERO; if (n > 0) return POS; } public static int add(int arg1, int arg2) { if (arg1==NEG && arg2==NEG) return NEG; if (arg1==NEG && arg2==ZERO) return NEG; if (arg1==ZERO && arg2==NEG) return NEG; if (arg1==ZERO && arg2==ZERO) return ZERO; if (arg1==ZERO && arg2==POS) return POS; if (arg1==POS && arg2==ZERO) return POS; if (arg1==POS && arg2==POS) return POS; return Bandera.choose(0,2); /* case (POS,NEG), (NEG,POS) */ }
Compiling In Abstractions DEOS Kernel (abstracted) class Thread class Thread int itsLastExecution; ... public void startChargingCPUTime(){ int cp=itsEvent.currentPeriod(); if(cp == itsLastExecution) { ... } Signs itsLastExecution; ... public void startChargingCPUTime(){ Signs cp=itsEvent.currentPeriod(); if(Signs.eq(cp,itsLastExecution)){ ... } class StartofPeriodEvent class StartofPeriodEvent int itsPeriodId = 0; ... public int currentPeriod() { return itsPeriodId; } public void pulseEvent(...) {... if(countDown == 0) { itsPeriodId=itsPeriodId + 1; ... } Signs itsPeriodId = ZERO; ... public Signs currentPeriod() { return itsPeriodId; } public void pulseEvent(...) {... if(countDown == 0) { itsPeriodId=Signs.add(itsPeriodId ,POS);... } DEOS Kernel SIGNS SIGNS SIGNS
Choice-bounded Search State space searched Undetectable Violation Detectable Violation choose() X X
Property Abstraction Property Abstraction (under-approximation) Program Abstraction (over-approximation) Property System Model Goal: If the abstract property holds on the abstract system, thenthe original property holds on the original system
Property Abstraction abstracted to (exactly) abstracted to (underapproximated) Basic Idea • Property (LTL) is converted to negation-normal form. • For each predicate (e.g., on integers) of the form P(x,c) where x is bound to abstraction A, we replace P(x,c) by a disjunction of cases that guarantee P(x,c) to be true. Examples (where x is bound to Signs) [](x > 0) [](x > -2) [](x == pos) [](x == zero || x == pos)
Heap Representation State Vector (heap) Scheduling (first P1 then P2) class Process1 extends Thread { public void run() { ... Object o1 = new Object(); ... } } class Process2 extends Thread { public void run() { ... Object o2 = new Object(); ... } } a b 2nd 1st a b
Heap Representation = These two states should be considered equal, but they have different representations = class Process1 extends Thread { public void run() { ... Object o1 = new Object(); ... } } class Process2 extends Thread { public void run() { ... Object o2 = new Object(); ... } } a b 1st 2nd State Vector (heap) Scheduling a b (first P1 then P2) b a (first P2 then P1)
Heap Issues Observationally Equivalent garbage but naïve representation yields distinct states Different thread interleavings may cause different positioning of heap objects. This will cause observationally equivalent heaps to be considered distinct states --- leading to tremendous state explosion. For avoiding state-space explosion when model-checking OO software, one needs a heap representation that identifies as many observationally equivalent heaps as possible!
Simple Representation collection for each allocator site … … … … … … = = ! class Process1 extends Thread { public void run() { ... Object o1 = new Object(); ... } } class Process2 extends Thread { public void run() { ... Object o2 = new Object(); ... } } a b l2 l1 Structured State Vector (regions/collections) Scheduling l1 l2 (first P1 then P2) a b (first P2 then P1) a b
Bounded Buffer BIR static identification of threads object state as record bounded integer values qualified lock representation “mini-heaps” – one per allocator site Reference type indicates mini-heaps that can be pointed to. Easily express results of “points-to” analysis State Declarations process BoundedB() BoundedBuffer_rec = record { bound : range -1..4; head : range -1..4; tail : range -1..4; BIRLock : lock wait reentrant; }; BoundedBuffer_col : collection [3] of BoundedBuffer_rec; BoundedBuffer_col_0 : collection [3] of BoundedBuffer_rec; BoundedBuffer_ref = ref { BoundedBuffer_col, BoundedBuffer_col_0 };
Bounded Buffer BIR control point label live variable informationused to optimize back-end code annotation denotinginvisible transition which canbe merged with followingtransition built-in operations on lock representations BIR Transitions loc s34: live { b2, b1, T_0, T_6, T_8 } when true do invisible { T_8 := (T_6 % T_8); } goto s35; … loc s36: live { b2, b1, T_0 } when true do { notifyAll(T_0.BIRLock); } goto s37; … loc s37: live { b2, b1, T_0 } when true do { unlock(T_0.BIRLock); } goto s38;