Learning objectives • Understand the purpose and appropriate uses of finite-state verification (fsv) – Understand how fsv mitigates weaknesses of testing Finite State Verification – Understand how testing complements fsv • Understand modeling for fsv as a balance between cost and precision • Distinguish explicit state enumeration from analysis of implicit models – And understand why implicit models are sometimes (but not always) more effective (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 1 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 2 Limits and trade-offs Resources and results Properties to • Most important properties of program execution be proved symbolic execution are undecidable in general complex and formal reasoning finite state • Finite state verification can automatically verification prove some significant properties of a finite model of the infinite execution space applies techniques from symbolic execution – balance trade-offs among and formal verification to models that abstract • generality of properties to be checked the potentially infinite state space of program behavior • class of programs or models that can be checked control into finite representations • computational effort in checking and data flow models • human effort in producing models and specifying properties simple Computational cost low high (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 3 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 4
Analysis of models Cost trade-offs ... public static Table 1 getTable 1() { if (ref == null ) { • Human effort and skill are required synchronized (Table 1) { if (ref == null ){ No concurrent ref = new Table 1(); – to prepare a finite state model ref .initialize (); modifications of } Table 1 } – to prepare a suitable specification for automated analysis } return ref ; }... • Iterative process: Direct check of source /design PROPERTY OF INTEREST PROGRAM or DESIGN (impractical or impossible ) – prepare a model and specify properties – attempt verification Derive models Implication of software – receive reports of impossible or unimportant faults or design – refine the specification or the model • Automated step Algorithmic check PROPERTY OF THE MODEL MODEL of the model for the property – computationally costly (a) (x) (b) (y) • computational cost impacts the cost of preparing model and never(<d>and <y>) specification, which must be tuned to make verification feasible (c) (d) – manually refining model and specification less expensive with (e) near-interactive analysis tools (f) (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 5 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 6 Defining the global state space – Applications for Finite State Verification Concurrent system example • Concurrent (multi-threaded, distributed, ...) • Deriving a good finite state model is hard – Difficult to test thoroughly (apparent non- • Example: finite state machine model of a determinism based on scheduler); sensitive to program with multiple threads of control differences between development environment and – Simplifying assumptions field environment • we can determine in advance the number of threads – First and most well-developed application of FSV • we can obtain a finite state machine model of each thread • Data models • we can identify the points at which processes can interact – State of the whole system model – Difficult to identify “corner cases” and interactions = tuple of states of individual process models among constraints, or to thoroughly test them – Transition = transition of one or more of the • Security individual processes, acting individually or in concert – Some threats depend on unusual (and untested) use (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 7 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 8
Concurrent system example – State space exploration – implementation Concurrent system example class Table1 { public void reinit () { needsInit = true; } private static Table1 ref = null; private boolean needsInit = true; • Specification: an on-line purchasing system private synchronized void private ElementClass [ ] initialize() { theValues; . . . – In-memory data structure initialized by reading private Table1() { } needsInit = false; configuration tables at system start-up } public static Table1 getTable1() { public int lookup (int i) { – Initialization of the data structure must appear atomic if (ref == null) if (needsInit) { { synchedInitialize(); } synchronized(this) { – The system must be reinitialized on occasion return ref; if (needsInit) { this.initialize(); } – The structure is kept in memory } } private static synchronized void • Implementation (with bugs): } synchedInitialize() { return theValues[i].getX() if (ref == null) { + theValues[i].getY(); – No monitor (Java synchronized ): too expensive* } ref = new Table1(); . . . ref.initialize(); – Double-checked locking idiom* for a fast system } } } *Bad decision, broken idiom ... but extremely hard to find the bug through testing. (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 9 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 10 A finite state machine model for each thread Analysis (a) (x) • Start from models of individual threads reinit() lookup() needsInit==true needsInit=true • Systematically trace all the possible (b) (y) interleavings of threads obtain lock • Like hand-executing all possible sequences of execution, E (c) but automated needsInit==true needsInit==false (d) needsInit==false modifying needsInit=false ... begin by constructing a finite state machine (e) model of each individual thread ... release lock (f) reading E (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 11 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 12
Express the model in Promela Analysis ... proctype Lookup(int id ) { • Java threading rules: if :: (needsInit) -> atomic { ! locked -> locked = true; }; – when one thread has obtained a monitor lock needsinit==true if :: (needsInit) -> assert (! modifying); – the other thread cannot obtain the same lock modifying = true; • Locking acquire lock /* Initialization happens here */ modifying = false ; – prevents threads from concurrently calling initialize needsInit = false; – Does not prevent possible race condition between ... :: (! needsInit) -> threads executing the lookup method skip; fi; • Tracing possible executions by hand is locked = false ; completely impractical fi; assert (! modifying);} (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 13 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 14 Run Spin; Inspect Output Interpret the trace proc 3 (lookup ) proc 1 (reinit) proc 2 (lookup ) Spin • Depth-first search of possible executions of the model public init lookup (int i) (a) if (needsInit ) { (b) • Explores 10 states and 51 state transitions in 0.16 seconds synchronized (this) { (c) if (needsInit ) { (d) • Finds a sequence of 17 transitions from the initial state of the this.initialize() ; (e) model to a state in which one of the assertions in the model } } evaluates to false } Depth=10 States=51 Transitions=92 Memory=2.302 public void reinit () (x ) pan: assertion violated !(modifying) (at depth 17) { needsInit = true ; } (y ) pan: wrote pan_in.trail (Spin Version 4.2.5 -- 2 April 2005) public init lookup (int i) (a) … if (needsInit ) { … (b) synchronized (this) { return (c) 0.16 real 0.00 user 0.03 sys theValues [i].getX () if (needsInit ) { (f) (d) Read/write this.initialize() ; + theValues [i].getY (); Race condition } ... States (f) and (d) (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 15 (c) 2007 Mauro Pezzè & Michal Young Ch 8, slide 16
Recommend
More recommend