Proof Automation for High Integrity Software - Heriot

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