Proving Exception Freedom within High Integrity Software Systems Bill J. Ellis ([email protected]) Dependable Systems Group Heriot-Watt University (Project page: http://www.macs.hw.ac.uk/~air/clamspark/) High Integrity Software • Software standards encourage or enforce proof for high integrity software: – MOD 00-55: requirements for the procurement of safety critical software in defence equipment. • Formal methods and proof mandatory – ITSEC: Information technology security evaluation criteria • Formal methods mandatory Praxis and SPARK • SPARK is developed by Praxis Critical Systems for building high integrity software: – Formally defined safe subset of Ada – Information and data flow static analysis – Supports proofs of: • Partial correctness • Exception freedom (No run time errors) • SPARK is used in industry: – BAE prove exception freedom (Unnamed project) – Praxis completed SHOLLIS and MULTOS CA – Many more... SPARK Proof In Industry • Partial correctness (Rare): – User supplied specification – Proofs usually deep – Very limited automation • Exception freedom (Increasingly common): – Automatic specification – Proofs usually shallow – Good (90%) automation via Praxis Simplifier – Remaining 10% may number in the thousands... Exception Freedom in SPARK • • • • Storage_Error Program_Error Tasking_Error Constraint_Error – – – – – – – Access_Check Discriminant_Check Tag_Check Division_Check Index_Check Range_Check Overflow_Check (Static memory requirement) (Some can occur in SPARK) Proving exception freedom in SPARK is proving variables stay within legal bounds Exception Freedom VCs Pre-condition Invariant Post-condition Check ... Check Prove properties hold between between cut points Check ... Check Prove exception freedom VCs using properties Check ... Check The Strategy Try proof again Prove Exception Freedom VCs Success! Fail Discover properties (Typically invariants) Prove properties Abstract Interpretation (AI) • Evaluate a program, replacing concrete variables with abstract values. – Concrete integer variable: -32768 to +32767 – An abstract integer variable: {-,0,+}. • Abstract interpretation provides a framework to reason about programs in the abstract. Abstracting to Bounds • Variable: – – – – type(lower, upper) equal(expression) fromto(lower, upper)... between(expression, expression)… • Array: – As many variables • Generalise across ranges where possible Example subtype Index is Integer range 0 .. 9; type D is array (Index) of Integer; R:=0; --#check For I in R>=-32768 Index loopand R<=32767; For if I in D(I) Index >= 0 loop and D(I) <= 100 then --# R Want := R to+discover D(I); invariant here! --#I>=0 end if; and I<=9; end if loop; D(I) >= 0 and D(I) <= 100 then R := R + D(I); --#check R>=-32768 and R<=32767; end if; end loop; Example subtype Index is Integer range 0 .. 9; type D is array (Index) of Integer; R:=0; --#check R>=-32768 and R<=32767; For I in Index loop --#invariant I>=0 and I<=9 and --#forall J in 0..9 D(J)>=-32768 and D(J)<=32767; --#check I>=0 and I<=9; if D(I) >= 0 and D(I) <= 100 then R := R + D(I); --#check R>=-32768 and R<=32767; end if; end loop; Example (AI Flowchart) Pre-condition R:=0 I:=0 Invariant N Loop junction node D(I) 0 and D(I) 100 I:=I+1 Normalised form of a SPARK for loop Simple junction node R:=R+D(I) I=9 Postcondition Recurrence Relations (Loop junction) • Liner recurrence relations with constant coefficients (LRRCs): – an=c1*an-1+…ck*an-k+f(n) • n>=k • Only use first powers of previous terms (an-1^1) • Coefficients ck are constants • f(n) is a function – Example: an=a(n-1) +1 • Can automatically solve LRRCs (and a few other special cases) using: – Mathematica – The Parma University's Recurrence Relation Solver (PURRS) – Others? Assignment • First assignment to a variable inside a loop: – Build recurrence relations. – The assignment A:=f(A) is recorded as A n = {equal(recurrence(f(A n-1 )))} • All other assignments: – Perform the assignment. – The assignment A:=f(A) is applied to all expressions in A. • Eliminate non-constants from expressions: – Generalise to extreme bounds. – Replace non-constant f(B) in A={equal(f(B))} with bounds of f(B). – A = {fromto(extreme-lower(B), extreme-upper(B))} Example (Variable R) Pre-condition R:=0 I:=0 Invariant N R={Type(-32768, 32767)} R={Type(-32768, 32767), equal(0)} D(I) 0 and D(I) 100 R n={Type(-32768, 32767), equal(recurrence(R (n-1)+D(I n))) between(recurrence(R (n-1)+0), recurrence(R (n-1)+100))} R n={Type(-32768, 32767), merge([equal(0), between(recurrence(R (n-1)+0), recurrence(R (n-1)+100))])} I:=I+1 R:=R+D(I) I=9 Postcondition Example (Variable R) recurrence(R (n-1)+0) Rn=R(n-1)+0 Rn=R0 0 R n={Type(-32768, 32767), equal(0)} recurrence(R (n-1)+100) Rn=R(n-1)+100 Rn=R0+100*n 0+100*n 100*n Arriving at the loop between(0, 100*n) n is in range 0 to infinity 0<=100*n R n={Type(-32768, 32767), merge([equal(0), between(recurrence(R (n-1)+0), recurrence(R (n-1)+100))])} Returning from first iteration fromto(0, 100*n) merge([equal(0), fromto(0, 100*n)]) equal(0) is inside fromto(0, 100*n) R n={Type(-32768, 32767), fromto(0, 100*n)} Starting second iteration... Property Discovery (Eliminate n) R n={Type(-32768, 32767), fromto(0, 100*n)} I n={Type(-32768, 32767), equal(n), fromtoexit(-32768+1, exc(9+1)), fromtoexit(exc(9+1), 32767+1)} Properties for R Express n in terms of I: I n=n n=I n Replace n with I in R: R n={Type(-32768, 32767), fromto(0, 100* I)} Some details... Rule out type: 0 -32768 100* I 32767 100* 9 32767 I 0 and I < 10 R 0 and R 100*I Properties for I Example (Discovered invariant) subtype Index is Integer range 0 .. 9; type D is array (Index) of Integer; R:=0; --#check R>=-32768 and R<=32767; For I in Index loop --#invariant I>=0 and I<=9 and R>=0 and R<=100*I and --#forall J in 0..9 D(J)>=-32768 and D(J)<=32767; --#check I>=0 and I<=9; if D(I) >= 0 and D(I) <= 100 then R := R + D(I); --#check R>=-32768 and R<=32767; end if; end loop; And the proofs? • Invariant property VCs: – Rippling reduces VC to a residue • Prove residue using proof planning • Exception freedom VCs: – Transitivity based proof planning Implementation (Underway…) SPARK code Add new properties to code Praxis Examiner VCs Method: Abstracting to bounds CLAM Method: Rippling Rule files NuSPADE Proof Planne r Light weight SPARK Parser SPARK structure Subprogram Spider Subprogram Details Proof scripts Related Work • RUNCHECK (Steven M. German) (1981) – – – – Proves exception freedom VCs for Pascal Uses a few rewrite rules (7) to solve recurrence relations as a final stage Does not exploit program context Limited treatment of arrays (Considered array initialisation) • Abstract Interpretation (Patrick Cousot, Radhia Cousot) (1976) – Is algorithmic and always generates correct results – No heuristics – Good automatic linear property generation for programs with linear assignments and no arrays – Used for compiler optimisation, exception detection, program documentation, program visualisation... Conclusions • Generate properties via (unsound) abstract interpretation: – Will contain heuristics – Exploit off the shelf recurrence relation solvers – More powerful (Include arrays, generate non-linear relationships) – Can fail! • Prove via: – Proof planning – Automated – Can fail! EOF
© Copyright 2026 Paperzz