def tryAndVerifyWithSplitterPreds(bbs, loop, old_sound_invs, boogie_invs, splitterPreds, partialInvs, timeout=None): """ Wrapper around tryAndVerify_impl that adds implication with the splitter predicates to all candidate invariants. Returns ((p1_overfit, p2_overfit), (p1_nonindg, p2_nonind), sound, violations) Where p1_overfit, p2_ovefit are lists of pairs of overfittted invariants and their respective counterexamples from passes 1 and 2 p1_nonind, p2_ovefit are lists of pairs of noninductive invariants and their respective counterexamples from passes 1 and 2 sound is a set of sound invariants violations is a list of any safety violations permitted by the sound invariants """ assert isinstance(old_sound_invs, set) assert isinstance(boogie_invs, set) assert isinstance(partialInvs, list) assert isinstance(splitterPreds, list) initial_sound = old_sound_invs.union(partialInvs) # First lets find the invariants that are sound without implication p1_overfitted, p1_nonind, p1_sound, violations =\ tryAndVerify_impl(bbs, loop, initial_sound, boogie_invs, timeout) p1_sound = \ set([x for x in p1_sound \ if not conservative_tautology(expr_to_z3(x, AllIntTypeEnv()))]) # Next lets add implication to all unsound invariants from first pass # Also add manually specified partialInvs unsound = [inv_ctr_pair[0] for inv_ctr_pair in p1_overfitted + p1_nonind] candidate_precedents = \ [ ast_and(pSet) for pSet in nonempty(powerset(splitterPreds)) ] p2_invs = [ AstBinExpr(precc, "==>", inv) for precc in candidate_precedents for inv in unsound ] + partialInvs p2_invs = \ set([ x for x in p2_invs \ if not conservative_tautology(expr_to_z3(x, AllIntTypeEnv())) ]) # And look for any new sound invariants p2_overfitted, p2_nonind, p2_sound, violations = \ tryAndVerify_impl(bbs, loop, p1_sound.union(set(partialInvs)), \ p2_invs, timeout) sound = p1_sound.union(p2_sound) return ((p1_overfitted, p2_overfitted), \ (p1_nonind, p2_nonind), \ sound, \ violations)
def execute(env, bb, bbs, limit): q = [ (expr_to_z3(env_to_expr(env), AllIntTypeEnv()), bb , SSAEnv(None, ""), [ ], [ ]) ] def bb_sp(bb, initial_ssa_env, precond): nd_path, final_env = nd_bb_path_to_ssa([bb], bbs, initial_ssa_env) sp = sp_nd_ssa_path(nd_path, bbs, precond, AllIntTypeEnv()) return simplify(sp), final_env, nd_path while len(q) > 0: precond, bb, ssa_env, curp, cur_ssap = q.pop() #print "Running ", bb, " with pre: ", precond, "env:", ssa_env.replm() postcond, after_env, ssaed_bb = bb_sp(bb, ssa_env, precond) if (not satisfiable(postcond)): continue newp = curp + [ bb ] new_ssap = cur_ssap + ssaed_bb if (len(bbs[bb].successors) == 0 or len(curp) + 1 >= limit): yield postcond, after_env, newp, new_ssap, \ extract_ssa_path_vars(new_ssap, model(postcond)) continue for s in bbs[bb].successors: q.append((postcond, s, deepcopy(after_env), newp, new_ssap))
def instantiate(invs, traceVars, trace, mturkId): #pylint: disable=unused-argument """ Given a set of invariant templates inv, a set of variables traceVars and concrete values for these variables trace, return a set of concrete instances of the invariant templates, that hold for the given traces. A concrete instatnce of a template is an invariant with the same shape, with variables and constants substituted in the appropriate places. Not currently in use. """ res = [] z3Invs = [] templates = [(esprimaToBoogie(x[0], {}), x[1], x[2]) for x in invs] vals = map(lambda x: _to_dict(traceVars, x), trace) for (bInv, symConsts, symVars) in templates: for instInv in instantiateAndEval(bInv, vals, symVars, symConsts): instZ3Inv = expr_to_z3(instInv, AllIntTypeEnv()) implied = False z3Inv = None for z3Inv in z3Invs: if implies(z3Inv, instZ3Inv): implied = True break if (implied): continue res.append(instInv) z3Invs.append(instZ3Inv) return map(boogieToEsprima, res)
def equiv(boogie1, boogie2): [p1, p2] = [expr_to_z3(pred, AllIntTypeEnv()) for pred in [boogie1, boogie2]] solv = getSolver() solv.add(Not(p1 == p2)) res = solv.check() return res == unsat
def wp_stmt(stmt, pred, typeEnv): if (isinstance(stmt, AstLabel)): stmt = stmt.stmt if (isinstance(stmt, AstAssignment)): assignee = str(stmt.lhs) # Should already be SSA-ed assert(assignee not in expr_read(stmt.rhs)) lhs = typeEnv[stmt.lhs](assignee) rhs = expr_to_z3(stmt.rhs, typeEnv) return z3.substitute(pred, (lhs, rhs)) elif (isinstance(stmt, AstAssert)): return And(pred, expr_to_z3(stmt.expr, typeEnv)) elif (isinstance(stmt, AstAssume)): return Implies(expr_to_z3(stmt.expr, typeEnv), pred) else: raise Exception("Cannot handle Boogie Statement: " + str(stmt))
def sp_stmt(stmt, pred, typeEnv): if (isinstance(stmt, AstLabel)): stmt = stmt.stmt if (isinstance(stmt, AstAssignment)): assignee = str(stmt.lhs) # Should already be SSA-ed assert(assignee not in expr_read(stmt.rhs) and \ (assignee not in map(str, ids(pred)))) lhs = typeEnv[stmt.lhs](assignee) rhs = expr_to_z3(stmt.rhs, typeEnv) return And(lhs == rhs, pred) elif (isinstance(stmt, AstAssert)): return And(pred, expr_to_z3(stmt.expr, typeEnv)) elif (isinstance(stmt, AstAssume)): return And(pred, expr_to_z3(stmt.expr, typeEnv)) else: raise Exception("Cannot handle Boogie Statement: " + str(stmt))
def simplifyInv(inv, mturkId): #pylint: disable=unused-argument """ Given an invariant inv return its 'simplified' version. We treat that as the canonical version of an invariant. Simplification is performed by z3 """ boogieInv = esprimaToBoogie(inv, {}) noDivBoogie = divisionToMul(boogieInv) z3_inv = expr_to_z3(noDivBoogie, AllIntTypeEnv()) print z3_inv, boogieInv simpl_z3_inv = simplify(z3_inv, arith_lhs=True) simpl_boogie_inv = z3_expr_to_boogie(simpl_z3_inv) return boogieToEsprima(simpl_boogie_inv)
exit(1) additionalInvs = {} if args.additionalInvs: with open(args.additionalInvs) as f: r = csv.reader(f, delimiter=",") for row in r: if not row: continue invs = [] for invstr in invlist.split(";"): if not len(invstr.strip()): continue try: inv = parseExprAst(invstr) if tautology(expr_to_z3(inv, AllIntTypeEnv())): print "Dropping additional invariant (tautology): ", inv continue except RuntimeError: # Some invariants are too large to parse print "Dropping additional invariant (parse): ", inv continue except Unknown: # Timeouts could be valid invariants pass invs.append(inv) additionalInvs[lvlName] = invs print "ADDITIONAL INVARIANTS LOADED FOR LVLS:", \ ", ".join(additionalInvs.keys()) print
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
args = p.parse_args(); s = open_sqlite_db("../logs/" + args.ename + "/events.db")() lvlsetName, lvls = loadBoogieLvlSet(args.lvlset) otherInvs = { } if (args.additionalInvs): with open(args.additionalInvs) as f: r = csv.reader(f, delimiter=","); for row in r: (lvl, invs) = row bInvs = [] for inv in [x for x in invs.split(";") if len(x.strip()) != 0]: try: bInv = parseExprAst(inv) if (tautology(expr_to_z3(bInv, AllIntTypeEnv()))): continue bInvs.append(bInv) except RuntimeError: # Some invariants are just too large for parsing :( pass except Unknown: bInvs.append(bInv) otherInvs[lvl]=bInvs lvlStats = { lvlN: { "usersStarted": set(),\ "nusersStarted": 0,\ "usersFinished": set(),\ "nusersFinished": 0,\
origName = justEnd.split(lvl_name)[0] originalToSplitM[origName] = originalToSplitM.get(origName, []) +\ [ lvl_name ] splitToOriginal[lvl_name] = origName print "Checking splitter predicates non-ovrelapping" for origName in originalToSplitM.keys(): # For now we assume each split level has a single splitterPred preds = [ unique(lvls[x]["splitterPreds"]) for x in originalToSplitM[origName] ] for i in xrange(0, len(preds)): for j in xrange(i + 1, len(preds)): if (not unsatisfiable( expr_to_z3(bast.ast_and([preds[i], preds[j]]), AllIntTypeEnv()))): print "Predicates ", preds[i], "and", preds[j], "from split of ", \ origName, "are not independent" print "Checking conjunction of partial predicates is a solution" for origName in originalToSplitM.keys(): # For now we assume each split level has a single splitterPred preds = [lvls[x]["partialInv"] for x in originalToSplitM[origName]] if (len(preds) == 1): print "Skipping ", origName, "- only 1 split level (no other side of split)" continue conj = bast.ast_and(preds) lvl = lvls[originalToSplitM[origName][0]] try: (overfit, nonind, sound, violations) = verify(lvl, [conj]) if (not len(violations) == 0):