def ssa_stmt(stmt, prev_replm, cur_replm): # Havoc's turn into no-ops when SSA-ed. if isinstance(stmt, AstHavoc): return AstAssert(AstTrue()) if isinstance(stmt, AstAssignment): return AstAssignment(replace(stmt.lhs, cur_replm), replace(stmt.rhs, prev_replm)) else: return replace(stmt, cur_replm)
def instantiateAndEval(inv, vals, var_names = None, const_names = None): if (var_names == None): var_names = ["_sv_x", "_sv_y", "_sv_z"] if (const_names == None): const_names = ["_sc_a", "_sc_b", "_sc_c"] res = [] symVs = [ x for x in expr_read(inv) if x in var_names ] symConsts = [ x for x in expr_read(inv) if x in const_names ] nonSymVs = set(expr_read(inv)).difference(set(symVs))\ .difference(set(symConsts)) traceVs = vals[0].keys() prms = permutations(range(len(traceVs)), len(symVs)) typeEnv = { str(x) + str(i) : Int for x in vals[0].keys() for i in xrange(len(vals)) } typeEnv.update({ str(c) : Int for c in symConsts }) for prm in prms: varM = { symVs[i]: traceVs[prm[i]] for i in xrange(len(symVs)) } varM.update({ nonSymV: nonSymV for nonSymV in nonSymVs }) inst_inv = replace(inv, { AstId(x) : AstId(varM[x]) for x in symVs }) p = [ AstAssume(env_to_expr(x, str(i))) for (i,x) in enumerate(vals) ] p += [ AstAssert(replace(inst_inv, { AstId(x) : AstId(x + str(i)) for x in varM.values() })) for i in xrange(len(vals)) ] m = maybeModel(And(map(lambda s: stmt_to_z3(s, typeEnv), p))) if (m): const_vals = { AstId(x) : AstNumber(m[x]) for x in symConsts } res.append(replace(inst_inv, const_vals)) return res
def filterCandidateInvariants(bbs, preCond, postCond, cutPoints, timeout=None): assert (len(cutPoints) == 1) entryBB = bbEntry(bbs) cps = {bb: set(cutPoints[bb]) for bb in cutPoints} cps[entryBB] = [preCond] overfitted = {bb: set([]) for bb in cps} nonind = {bb: set([]) for bb in cps} # The separation in overfitted and nonind is well defined only in the # Single loop case. So for now only handle these. Can probably extend later aiTyEnv = AllIntTypeEnv() cpWorkQ = set([entryBB] + cps.keys()) while (len(cpWorkQ) > 0): cp = cpWorkQ.pop() cp_inv = expr_to_z3(ast_and(cps[cp]), aiTyEnv) initial_path, intial_ssa_env = nd_bb_path_to_ssa([cp], bbs, SSAEnv()) pathWorkQ = [(initial_path, intial_ssa_env, cp_inv)] # Pass 1: Widdle down candidate invariants at cutpoints iteratively while len(pathWorkQ) > 0: path, curFinalSSAEnv, sp = pathWorkQ.pop(0) nextBB, nextReplMaps = path[-1] processedStmts = [] ssa_stmts = _ssa_stmts(bbs[nextBB].stmts, nextReplMaps) for s in ssa_stmts: if (isinstance(s, AstAssert)): pass # During the first pass we ignore safety violations. We just # want to get an inductive invariant network elif (isinstance(s, AstAssume)): try: if (unsatisfiable(And(sp, expr_to_z3(s.expr, aiTyEnv)), timeout)): break except Unknown: pass # Conservatively assume path is possible on timeout processedStmts.append(s) new_sp = sp_stmt(s, sp, aiTyEnv) #print "SP: {", sp, "} ", s, " {", new_sp, "}" sp = new_sp if (len(processedStmts) != len(bbs[nextBB].stmts)): # Didn't make it to the end of the block - path must be unsat continue if (len(bbs[nextBB].successors) == 0): # This is exit assert nextBB == bbExit(bbs) # During Pass 1 we don't check the postcondition is implied else: for succ in bbs[nextBB].successors: if succ in cps: # Check implication start = initial_path[0][0] candidate_invs = copy(cps[succ]) for candidate in candidate_invs: candidateSSA = expr_to_z3( replace(candidate, curFinalSSAEnv.replm()), aiTyEnv) try: c = counterex(Implies(sp, candidateSSA), timeout, "Candidate: " + str(candidate)) except Unknown: c = {} # On timeout conservatively assume fail if (c != None): v = Violation("inductiveness", path + [(succ, None)], [], Implies(sp, candidateSSA), c) if (start == entryBB): overfitted[succ].add((candidate, v)) else: nonind[succ].add((candidate, v)) cps[succ].remove(candidate) if (len(candidate_invs) != len(cps[succ])): cpWorkQ.add(succ) else: assert succ not in path # We should have cutpoints at every loop succSSA, nextFinalSSAEnv = \ nd_bb_path_to_ssa([succ], bbs, SSAEnv(curFinalSSAEnv)) pathWorkQ.append((path + succSSA, nextFinalSSAEnv, sp)) sound = cps # Pass 2: Check for safety violations violations = checkInvNetwork(bbs, preCond, postCond, sound, timeout) for v in violations: if (not v.isSafety()): print v assert (v.isSafety()) # sound should be an inductive network return (overfitted, nonind, sound, violations)
def checkInvNetwork(bbs, preCond, postCond, cutPoints, timeout=None): cps = copy(cutPoints) entryBB = bbEntry(bbs) cps[entryBB] = [preCond] aiTyEnv = AllIntTypeEnv() violations = [] for cp in cps: initial_path, intial_ssa_env = nd_bb_path_to_ssa([cp], bbs, SSAEnv()) workQ = [(initial_path, intial_ssa_env, expr_to_z3(ast_and(cps[cp]), aiTyEnv))] while len(workQ) > 0: path, curFinalSSAEnv, sp = workQ.pop(0) nextBB, nextReplMaps = path[-1] processedStmts = [] ssa_stmts = zip(_ssa_stmts(bbs[nextBB].stmts, nextReplMaps), nextReplMaps) for (s, replM) in ssa_stmts: if (isinstance(s, AstAssert)): try: c = counterex(Implies(sp, expr_to_z3(s.expr, aiTyEnv)), timeout) except Unknown: c = {} # On timeout conservatively assume fail if (c != None): # Current path can violate assertion v = Violation("safety", path, processedStmts + [(s, replM)], Implies(sp, expr_to_z3(s.expr, aiTyEnv)), c) violations.append(v) break elif (isinstance(s, AstAssume)): try: if (unsatisfiable(And(sp, expr_to_z3(s.expr, aiTyEnv)), timeout)): break except Unknown: pass # Conservatively assume path is possible on timeout processedStmts.append((s, replM)) new_sp = sp_stmt(s, sp, aiTyEnv) #print "SP: {", sp, "} ", s, " {", new_sp, "}" sp = new_sp if (len(processedStmts) != len(bbs[nextBB].stmts)): # Didn't make it to the end of the block - path must be unsat or # violation found continue if (len(bbs[nextBB].successors) == 0): # This is exit assert nextBB == bbExit(bbs) postSSA = expr_to_z3(replace(postCond, curFinalSSAEnv.replm()), aiTyEnv) try: c = counterex(Implies(sp, postSSA), timeout) except Unknown: c = {} # On timeout conservatively assume fail if (c != None): v = Violation("safety", path, processedStmts, Implies(sp, postSSA), c) violations.append(v) else: for succ in bbs[nextBB].successors: if succ in cps: # Check implication post = ast_and(cps[succ]) postSSA = replace(post, curFinalSSAEnv.replm()) postSSAZ3 = expr_to_z3(postSSA, aiTyEnv) try: c = counterex(Implies(sp, postSSAZ3), timeout) except Unknown: try: # Sometimes its easier to check each post-invariant # individually rather than all of them at once (similarly # to the filter case). Try this if the implication of the # conjunction of all of them fails for p in cps[succ]: postSSA = replace(p, curFinalSSAEnv.replm()) postSSAZ3 = expr_to_z3(postSSA, aiTyEnv) c = counterex(Implies(sp, postSSAZ3), timeout) # If any of them doesn't hold, neither does their conj if (c != None): break except Unknown: c = {} # On timeout conservatively assume fail if (c != None): v = Violation("inductiveness", path + [(succ, None)], [], Implies(sp, postSSAZ3), c) violations.append(v) else: assert succ not in path # We should have cutpoints at every loop succSSA, nextFinalSSAEnv = \ nd_bb_path_to_ssa([succ], bbs, SSAEnv(curFinalSSAEnv)) workQ.append((path + succSSA, nextFinalSSAEnv, sp)) return violations
def substitutions(expr, replMs): family = set([expr]) for replM in replMs: family.add(replace(expr, replM)) return family