340 likes | 485 Views
A SAT characterization of boolean-program correctness. K. Rustan M. Leino Microsoft Research, Redmond, WA. 14 Nov 2002 IFIP WG 2.4 meeting, Schlo β Dagstuhl, Germany. Motivation. This program has performed an illegal operation. If the problem persists, please contact the vendor.
E N D
A SAT characterization of boolean-program correctness K. Rustan M. LeinoMicrosoft Research, Redmond, WA 14 Nov 2002IFIP WG 2.4 meeting, Schloβ Dagstuhl, Germany
Motivation This program has performed an illegal operation. If the problem persists, please contact the vendor.
The SLAM toolkit Tom Ball, Sriram Rajamani, et al., Microsoft Research Properties of interest: x “resource is locked” y t > 0 Device driver (C program) Abstraction (boolean program) APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} y := false; assert x; x := true; x := x y; Predicate abstraction
The SLAM toolkit Tom Ball, Sriram Rajamani, et al., Microsoft Research Device driver (C program) Abstraction (boolean program) APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} y := false; assert x assert x; x := true; x := x y;
The SLAM toolkit Tom Ball, Sriram Rajamani, et al., Microsoft Research Device driver (C program) Abstraction (boolean program) APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} y := false; assert x assert x; x := true; x := x y; real error
The SLAM toolkit Tom Ball, Sriram Rajamani, et al., Microsoft Research Device driver (C program) Abstraction (boolean program) APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} y := false; assert x; x := true; x := x y; infeasible path
The SLAM toolkit Tom Ball, Sriram Rajamani, et al., Microsoft Research Properties of interest: x “resource is locked” y t > 0 z p≠ NULL Device driver (C program) Abstraction (boolean program) APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} APIERR DevGetStatus(LPSTATUS status){ unsigned int t; struct _Info info; for (i = 0; i < SPIGOT_COUNT; i++) { APIERR err; info.cbSize = sizeof(struct _Info); info.iSpigot = i; err = W_GetStatus(&info); if (err != APIERR_Success) { return err; } t |= info.fStatus & SPIGOT_ALIVE; } status->result = t | 0x2055; return APIERR_Success;} APIERR W_GetStatus(struct _Info * pinfo){ APIERR err; bool fChanged = TRUE; do { err = DevRegisterColumn(pinfo, TRUE); if (err != APIERR_Success) { return err; } if (pinfo->huwMagi < 10) { fChanged = FALSE; } else { err = DevReleaseColumn(pinfo); } } while (fChanged); return DevReleaseColumn(pinfo);} y := false; assert x; x := true; z := true; Predicate abstraction x := x y; Predicate abstraction if (z) …
Boolean programs • Prog ::= var Id* ; Block* • Block ::= LabelId : Stmt* goto LabelId* • Stmt ::= Id := Expr | assert Expr | assume Expr • Expr ::= false | true | Id | Expr | Expr Expr | Expr Expr
Example • var x, y;A: x := true; goto BB: assert x; x := x y; goto B or CC: A: x := true B: assert x; x := x y C:
Semantics: Weakest preconditions • For any statement S and postcondition Q, wp(S,Q) characterizes those pre-states from which execution is guaranteed: • not to go wrong, and • either the execution doesn’t terminate or it terminates in a state satisfying Q Q S wp(S,Q)
Semantics: Weakest preconditions • wp(x := E, Q) = Q[x:=E]wp(assert E, Q) = E Qwp(assume E, Q) = E Q • wp(skip, Q) = Qwp(S;T, Q) = wp(S, wp(T,Q)) • wp(gotolabels, Q) = ( L labels :: wp(L,Q))
Semantics of blocks What I write: • For any block: var w; … L: S; goto labels …introduce a boolean function L, such that: What I really mean: L(w) = wp(S, (Glabels :: G(w))) (w :: L(w) = wp(S, (Glabels :: G(w))) ) or equivalently: L = (λw :: wp(S, (Glabels :: G(w))) )
Example • A(x,y) = wp(x := true, B(x,y))B(x,y) = wp(assert x; x := x y, B(x,y) C(x,y))C(x,y) = wp(skip, true) • A(x,y) = B(true,y)B(x,y) = x B(x y, y) C(x y, y)C(x,y) = true A: x := true B: assert x; x := x y C:
Equations with multiple solutions A, B, C : A(x,y) = B(true, y) B(x,y) = x B(xy, y) C(xy, y) C(x,y) = true The unknowns Solution 0: A(x,y) = false B(x,y) = false C(x,y) = true Solution 1: A(x,y) = y B(x,y) = x y C(x,y) = true We want the weakest solution
Weakest solution A,B,C : A(x,y) = B(true, y) B(x,y) = x B(xy, y) C(xy, y) C(x,y) = true
Weakest solution A,B,C : A = (λx,y :: B(true, y)) B = (λx,y :: x B(xy, y) C(xy, y)) C = (λx,y :: true) F(A,B,C) G(A,B,C) H(A,B,C)
Weakest solution/fixpoint A,B,C : A = F(A,B,C) B = G(A,B,C) C = H(A,B,C) where F = (λx,y :: B(true, y)) G = (λx,y :: x B(xy, y) C(xy, y)) H = (λx,y :: true) (λx,y :: B(true, y)) (λx,y :: x B(xy, y) C(xy, y)) Weakest solution of A,B,C (λx,y :: true) F(A,B,C) F G(A,B,C) G H(A,B,C) H Weakest fixpoint of F,G,H
Program correctness • A program with variables w and start block A is correct iff: (w :: A(w)) • That is, the program has an error iff: A(w)is satisfiable. boolean equations, satisfiability SAT SAT functions, weakest solutions
Equations over a closed set of “terms” • Using the definitions: A(x,y) = B(true, y) B(x,y) = x B(xy, y) C(xy, y) C(x,y) = true • We produce: • A(x,y) = B(true, y) • B(true, y) = true B(truey, y) C(truey, y) • B(truey, y) = truey B(trueyy, y) C(trueyy, y) • C(truey, y) = true
Point functions • A function: f(w) = f(false) f(w)can be expressed as two point functions : ffalse = ffalse ffalse ftrue = ffalse ftrue
Point-function equations • A set of equations: f : f(w) = f(false) f(w)can be expressed as: ffalse, ftrue : ffalse = ffalse ffalse ftrue = ffalse ftrue
A fixpoint theorem • Given a function F on a complete lattice,if F is continuous, then its weakest fixpoint exists and is given by: Fk( T )for some natural number k. • k is the fixpoint depth of F • fixpoint depth lattice height T lattice height F k ( T )
Computing fixpoints: outward • T { apply F } • F(T) { apply F } • F(F(T)) { apply F } • F(F(F(T))) { apply F } • F(F(F(F(T)))) Suppose fixpoint depth of F is 3 produced in previous step equal to each other—weakest fixpoint of F
Computing fixpoints: inward • { replace with F() } • F() { replace with F() } • F(F()) { replace with F() } • F(F(F())) { replace with T } • F(F(F(T))) Suppose fixpoint depth of F is 3 produced in previous step no need for further applications of F weakest fixpoint of F
Multiple unknowns • a,b : a = F(a,b) b = G(a,b) • Suppose fixpoint depths of F,G are 2,1 • Weakest solution for a is: • a00 • F(a10 , b10 ) • F(F(a20, b20 ), G(a11 , b11)) • F(F(T , G(a21, b21)), G(F(a21, b21), T )) • F(F(T , G(T , T )), G(F(T , T ), T )) Number of enclosing applications of G Number of enclosing applications of F
Special instance • Lattice of booleans has height 1 • If F returns a boolean, then F’s fixpoint depth is at most 1 • and so F’s weakest fixpoint is F(T)
Back to our problem • Using the definitions: A(x,y) = B(true, y) B(x,y) = x B(xy, y) C(xy, y) C(x,y) = true • We produce: • Ax,y = Btrue,y • Btrue,y = true Btruey,y Ctruey,y • By,y = y Byy,y Cyy,y true
Back to our problem • Using the definitions: A(x,y) = B(true, y) B(x,y) = x B(xy, y) C(xy, y) C(x,y) = true • We produce: • Ax,y = Btrue,y • Btrue,y = true Btruey,y Ctruey,y • By,y = y true Cyy,y • Cy,y = true
Leibniz constraints • Being a function means, for any f: (w,w’ :: (w=w’) (f(w)=f(w’)))“Leibniz’s rule” • So when we have: Btrue,y = … By,y = …we also generate the following Leibniz constraint : (true=y) (y=y) (Btrue,y = By,y)
SAT formula • From the closed set of terms:Ax,y = Btrue,yBtrue,y = true Btruey,y Ctruey,yBy,y = y true Cyy,yCy,y = true we produce the following SAT equations: a a = b b = true b’ c b’ = y true c c = true y (b=b’) a b b’ c negated start symbol: A(x,y) Leibniz constraint
Summary • Boolean program, whose semantics is defined by weakest solution of weakest-precondition equations • Translate to SAT problem: • Instantiate equations to get a closed set of terms • Replace nested recursive instantiations by true • Conjoin negated start symbol and Leibniz constraints • Write point functions as propositional variables • Check for satisfiability • Performance? Heuristics? • Are Leibniz constraints really needed? • Better encoding of procedures?
Complexity • With N blocks and K variables: • each boolean function has K arguments, each a boolean expression • there are 22K different boolean expressions • So, there are N·2K·2K different terms • Suppose each of the 2K initial states were considered individually: • each boolean-function argument can then be folded into one of the 2 boolean constants • Then, there are only 2K·N·2K different terms Symbolic checking: Explicit-state checking:
Symbolic vs. explicit-state checking • The following equality (and others?) can be exploited heuristically to try to get a good balance: f(P, Q, R)=(P f(true, Q, R)) (P f(false, Q, R))
Summary • Boolean program, whose semantics is defined by weakest solution of weakest-precondition equations • Translate to SAT problem: • Instantiate equations to get a closed set of terms • Replace nested recursive instantiations by true • Conjoin negated start symbol and Leibniz constraints • Write point functions as propositional variables • Check for satisfiability • Performance heuristics: symbolic vs. explicit-state • Other heuristics? • Are Leibniz constraints really needed? • Better encoding of procedures?