def count(smt_file): formula = z3.parse_smt2_file(smt_file) tactic_simp = z3.With(z3.Tactic('simplify'), 'elim_and', True) tactic_total = z3.Then(tactic_simp, z3.Tactic('elim-term-ite')) tactic_total = z3.Then(tactic_total, z3.Tactic('tseitin-cnf')) goals = tactic_total(formula) if len(goals) == 1: goal = goals[0] return len(goal) else: return -1
def simplify(self, expr): if expr._simplified: return expr if self._enable_simplification_cache: try: k = self._simplification_cache_key[expr._cache_key] #print "HIT WEAK KEY CACHE" return k except KeyError: pass try: k = self._simplification_cache_val[expr._cache_key] #print "HIT WEAK VALUE CACHE" return k except KeyError: pass #print "MISS CACHE" l.debug("SIMPLIFYING EXPRESSION") #print "SIMPLIFYING" expr_raw = self.convert(expr) #l.debug("... before: %s (%s)", expr_raw, expr_raw.__class__.__name__) if isinstance(expr_raw, z3.BoolRef): tactics = z3.Then(z3.Tactic("simplify"), z3.Tactic("propagate-ineqs"), z3.Tactic("propagate-values"), z3.Tactic("unit-subsume-simplify")) s = tactics(expr_raw).as_expr() n = s.decl().name() if n == 'true': s = True elif n == 'false': s = False elif isinstance(expr_raw, z3.BitVecRef): s = z3.simplify(expr_raw) else: s = expr_raw for b in _eager_backends: try: o = self.wrap(b.convert(s)) break except BackendError: continue else: o = self._abstract(s) #print "SIMPLIFIED" #l.debug("... after: %s (%s)", s, s.__class__.__name__) o._simplified = Base.FULL_SIMPLIFY if self._enable_simplification_cache: self._simplification_cache_val[expr._cache_key] = o self._simplification_cache_key[expr._cache_key] = o return o
def check_equivalence(prog_before, prog_after, allow_undef): # The equivalence check of the solver # For all input packets and possible table matches the programs should # be the same z3_prog_before, input_names_before, _ = prog_before z3_prog_after, input_names_after, _ = prog_after undef_violation = False try: z3_prog_before = z3.simplify(z3_prog_before) z3_prog_after = z3.simplify(z3_prog_after) # the equivalence equation log.debug("Simplifying equation...") tv_equiv = z3.simplify(z3_prog_before != z3_prog_after) except z3.Z3Exception as e: # Encountered an exception when trying to compare the formulas # There might be many reasons for this error_string = "Failed to compare Z3 formulas!\nReason: %s" % e error_string += "\nPROGRAM BEFORE\n" error_string += get_hdr_table(z3_prog_before, input_names_before) error_string += "\n\nPROGRAM AFTER\n" error_string += get_hdr_table(z3_prog_after, input_names_after) log.error(error_string) return util.EXIT_VIOLATION log.debug("Checking...") log.debug(z3.tactics()) t = z3.Then( z3.Tactic("simplify"), # z3.Tactic("distribute-forall"), # z3.Tactic("ackermannize_bv"), # z3.Tactic("bvarray2uf"), # z3.Tactic("card2bv"), # z3.Tactic("propagate-bv-bounds-new"), # z3.Tactic("reduce-bv-size"), # z3.Tactic("qe_rec"), z3.Tactic("smt"), ) solver = t.solver() log.debug(solver.sexpr()) ret = solver.check(tv_equiv) log.debug(tv_equiv) log.debug(ret) if allow_undef and ret == z3.sat: undef_violation = True # if we allow undefined changes we need to explicitly recheck log.info("Detected difference in undefined behavior. " "Rechecking while substituting undefined variables.") ret = undef_check(solver, z3_prog_before, z3_prog_after) if ret == z3.sat: print_validation_error(prog_before, prog_after, solver.model()) return util.EXIT_VIOLATION elif ret == z3.unknown: log.error("Solution unknown! There might be a problem...") return util.EXIT_VIOLATION else: if undef_violation: return util.EXIT_UNDEF return util.EXIT_SUCCESS
def convert_to_dimacs(goal, project_onto): ''' Convert a Z3 goal into DIMACS so that ApproxMC can understand it. ''' # Based on https://stackoverflow.com/a/33860849 bits = set() for var in project_onto: nbits = var.sort().size() # Give a name to each bit of each bitvector for bit_index in range(nbits): name = '%s_%d' % (var, bit_index) bits.add(name) bit = z3.Bool(name) mask = z3.BitVecVal(1 << bit_index, nbits) goal.add(bit == ((var & mask) == mask)) # z3.With(..., args) provides arguments to the "..." tactic. tactic = z3.Then( 'simplify', z3.With( z3.Tactic('bit-blast'), blast_full=True, blast_quant=True, ), 'blast-term-ite', z3.With( z3.Tactic('propagate-values'), blast_eq_value=True, blast_distinct=True, ), z3.With( z3.Tactic('simplify'), blast_eq_value=True, ), 'blast-term-ite', 'tseitin-cnf', 'blast-term-ite', ) expr = tactic(goal) assert len(expr) == 1 expr = expr[0] dimacs = expr.dimacs() # ind is the Independent Set ind = set() # Parse the dimacs to determine how Z3 maps the boolean variables to the # dimacs variable number lines = dimacs.split('\n') for line in lines: if not line.startswith('c '): # It's not a comment. continue # Otherwise assume that this line maps variables to names parts = line.split() _, number, var = parts if var in bits: bits.remove(var) ind.add(number) # TODO: will this always be true? assert len(bits) == 0, repr(bits) return 'c ind %s 0\n%s\n' % (' '.join(ind), dimacs)
def _search_simplify_tactics(self): try: return self._tls.search_simplify_tactics except AttributeError: tactics = z3.Then(z3.Tactic("simplify", ctx=self._context), z3.Tactic("aig", ctx=self._context), ctx=self._context) self._tls.search_simplify_tactics = tactics return self._tls.search_simplify_tactics
def build_test(config, main_formula, cond_tuple, pkt_range): permut_conds, avoid_conds, undefined_conds = cond_tuple # now we actually verify that we can find an input s = z3.Solver() # bind the output constant to the output of the main program output_const = z3.Const("output", main_formula.sort()) s.add(main_formula == output_const) undefined_matches = z3.And(*undefined_conds) s.add(undefined_matches) avoid_matches = z3.Not(z3.Or(*avoid_conds)) s.add(avoid_matches) # we need this tactic to find out which values will be undefined at the end # or which headers we expect to be invalid # the tactic effectively simplifies the formula to a single expression # under the constraints we have defined t = z3.Then(z3.Tactic("propagate-values"), z3.Tactic("ctx-solver-simplify"), z3.Tactic("elim-and")) # this is the test string we assemble stf_str = "" for permut in permut_conds: s.push() s.add(permut) log.info("Checking for solution...") ret = s.check() if ret == z3.sat: log.info("Found a solution!") # get the model m = s.model() # this does not work well yet... desperate hack # FIXME: Figure out a way to solve this, might not be solvable g = z3.Goal() g.add(main_formula == output_const, avoid_matches, undefined_matches, z3.And(*permut)) log.debug(z3.tactics()) log.info("Inferring simplified input and output") constrained_output = t.apply(g) log.info("Inferring dont-care map...") # FIXME: horrible output_var = constrained_output[0][0].children()[0] dont_care_map = get_dont_care_map(config, output_var, pkt_range) input_hdr = m[z3.Const(config["ingress_var"], output_const.sort())] output_hdr = m[output_const] log.debug("Output header: %s", output_hdr) log.debug("Input header: %s", input_hdr) flat_input = input_hdr.children()[pkt_range] flat_output = output_hdr.children()[pkt_range] stf_str += get_stf_str(flat_input, flat_output, dont_care_map) stf_str += "\n" else: # FIXME: This should be an error log.warning("No valid input could be found!") s.pop() # the final stf string lists all the interesting packets to test return stf_str
def simplify(self, expr): if expr._simplified: return expr if self._enable_simplification_cache: try: k = self._simplification_cache_key[expr._cache_key] #print "HIT WEAK KEY CACHE" return k except KeyError: pass try: k = self._simplification_cache_val[expr._cache_key] #print "HIT WEAK VALUE CACHE" return k except KeyError: pass #print "MISS CACHE" l.debug("SIMPLIFYING EXPRESSION") #print "SIMPLIFYING" expr_raw = self.convert(expr) #l.debug("... before: %s (%s)", expr_raw, expr_raw.__class__.__name__) #s = expr_raw if isinstance(expr_raw, z3.BoolRef): tactics = z3.Then( z3.Tactic("simplify", ctx=self._context), z3.Tactic("propagate-ineqs", ctx=self._context), z3.Tactic("propagate-values", ctx=self._context), z3.Tactic("unit-subsume-simplify", ctx=self._context), z3.Tactic("aig", ctx=self._context), ctx=self._context ) s = tactics(expr_raw).as_expr() #n = s.decl().name() #if n == 'true': # s = True #elif n == 'false': # s = False elif isinstance(expr_raw, z3.BitVecRef): s = z3.simplify(expr_raw) else: s = expr_raw o = self._abstract(s) o._simplified = Base.FULL_SIMPLIFY if self._enable_simplification_cache: self._simplification_cache_val[expr._cache_key] = o self._simplification_cache_key[expr._cache_key] = o return o
def __init__(self, eqns=None, tactics=None, timeout=None): self.eqns = [] self.locs = [] if tactics: s = z3.TryFor(z3.Then(*tactics), 1000).solver() else: s = z3.Solver() if timeout: s.set(timeout=1000) self.solver = s if eqns: self.add(eqns) self._ctr = 0
def get_boolref_tactics(self): tactics = z3.Then( z3.Tactic("simplify"), z3.Tactic("sat-preprocess"), z3.Tactic("cofactor-term-ite"), z3.Tactic("propagate-ineqs"), z3.Tactic("propagate-values"), z3.Tactic("unit-subsume-simplify"), z3.Tactic("aig"), ) return tactics
def _boolref_tactics(self): try: return self._tls.boolref_tactics except AttributeError: tactics = z3.Then(z3.Tactic("simplify", ctx=self._context), z3.Tactic("propagate-ineqs", ctx=self._context), z3.Tactic("propagate-values", ctx=self._context), z3.Tactic("unit-subsume-simplify", ctx=self._context), z3.Tactic("aig", ctx=self._context), ctx=self._context) self._tls.boolref_tactics = tactics return self._tls.boolref_tactics
def calculate(input_smt): formula = z3.parse_smt2_file(input_smt) tactic_simp = z3.With(z3.Tactic('simplify'), 'elim_and', True) tactic_total = z3.Then(tactic_simp, z3.Tactic('elim-term-ite')) tactic_total = z3.Then(tactic_total, z3.Tactic('tseitin-cnf')) goals = tactic_total.apply(formula) if len(goals) == 1: goal = goals[0] # the goal is the list of constraints, and conjunction of which is equivalent to the original problem manager = compute(goal) # compute sparseness num_visible = manager.get_visible_size() num_var = manager.get_var_size() num_total = manager.get_total_size() num_min = manager.get_maximum_clause_size() sparse = (num_visible - num_min) / (num_total - num_min) factor_min = math.ceil((1 + math.sqrt(1 + 8 * num_visible)) / 2) factor_max = 2 * num_total factor = (num_var - factor_min) / (factor_max - factor_min) return sparse, factor else: return '*', '*'
def __init__(self): """ Class construction. A formula is meant to be instanciated via the static methods from_z3, from_smt2_file, from_dimacs_string and from_dimacs_file. """ self.goal = z3.Goal() self.tactics = z3.Then('simplify', 'bit-blast', 'tseitin-cnf') self.subgoal = None # keep it in case of # Goal is a collection of constraints we want to find a solution or show to be unsatisfiable (infeasible). # Goals are processed using Tactics. A Tactic transforms a goal into a set of subgoals. # A goal has a solution if one of its subgoals has a solution. # A goal is unsatisfiable if all subgoals are unsatisfiable. self.variables_num = None self.clauses_num = None self.clauses = []
def __init__(self, types, cons): super().__init__() assert isinstance(types, dict), 'Types for SMT must be a dictionary' assert isinstance(cons, list), 'Constraints for SMT must be a list' self.types = types self.cons = cons self.asmpts = [] # Some may call this the dark side but # put SMT variables in object locals, so that we can refer to them # at other places. single = 'z3.Float32()' double = 'z3.Float64()' BV32 = 'z3.BitVecSort(32)' BV64 = 'z3.BitVecSort(64)' RNE = 'z3.RNE()' for v in list(self.types.keys()): assert v not in vars(self) if self.types[v] == 'float': dym_str = 'vars(self)["{}"] = z3.Const("{}", {})'.format( v, v, double) else: assert self.types[v] == 'int' dym_str = 'vars(self)["{}"] = z3.fpSignedToFP({}, z3.Const("{}", {}), {})'.format( v, RNE, v, BV64, double) exec(dym_str) # TODO Cannot find reference to Tactic, check for problems t1 = z3.Tactic('simplify') t2 = z3.Tactic('solve-eqs') t3 = z3.Tactic('split-clause') t4 = z3.Tactic('qffpbv') t5 = z3.Tactic('qfnra-nlsat') t6 = z3.Tactic('normalize-bounds') t7 = z3.Tactic('smt') goal = z3.Then(z3.AndThen(t1, t2, t6), t4) # goal = z3.AndThen(t1, t2, t6) self.solver = goal.solver() for con in self.cons: trans_con = self.__transform(con) self.solver.add(trans_con)
def test_example_negate_self(self): if not os.path.exists(definitions.EXAMPLE_PATH): print('{} does not exist! Exiting...'.format( definitions.EXAMPLE_PATH)) exit(1) subprocess.call( [definitions.EXAMPLE_PATH, os.path.abspath(TEST1_FILE)]) z3_fml, seen_lits = utils.dimacs2fml(TEST1_FILE) # print('[{}] z3 formula: {}'.format(self._testMethodName, z3_fml)) print('[{}] #vars: {}'.format(self._testMethodName, seen_lits)) self.assertEqual(len(seen_lits), 17) g = z3.Goal() neg_z3_fml = z3.Not(z3.And(z3_fml)) # print('[{}] Negated formula: {}'.format(self._testMethodName, neg_z3_fml)) g.add(neg_z3_fml) t = z3.Then('simplify', 'tseitin-cnf') neg_cnf = t(g)[0] #print('[{}] Negated CNF: {}'.format(self._testMethodName, neg_cnf)) utils.translate2dimacs(neg_cnf, len(seen_lits), TEST1_FILE, append_to_file=True) num_vars, num_clauses = utils.parse_dimacs_header(TEST1_FILE) print('[{}] Formula and not formula num_vars {}, num_clauses \ {}'.format(self._testMethodName, num_vars, num_clauses)) fml, seen_lits = utils.dimacs2fml(TEST1_FILE) s = z3.Solver() s.add(fml) sol = s.check() self.assertEqual(sol, z3.unsat)
#!/usr/bin/python3 import pycparser as pycp import z3 import sys t_qe = z3.Tactic('qe') t = z3.Repeat( z3.Then("simplify", "propagate-ineqs", "propagate-values", "unit-subsume-simplify")) t_qe_ = z3.Then(t_qe, t) def gen_smt_expr(ast): if isinstance(ast, pycp.c_ast.Constant): return z3.BitVecVal(ast.value, 32) elif isinstance(ast, pycp.c_ast.ID): return vars[ast.name] elif isinstance(ast, pycp.c_ast.UnaryOp): if ast.op == "-": return "-" + gen_smt_expr(ast.expr) if ast.op == "!": return z3.Not(gen_smt_expr(ast.expr)) elif isinstance(ast, pycp.c_ast.BinaryOp): lexp = gen_smt_expr(ast.left) rexp = gen_smt_expr(ast.right) if ast.op == "+": return lexp + rexp if ast.op == "-": return lexp - rexp elif ast.op == "*":
#!/usr/bin/python3 import pycparser as pycp import z3 import sys import random t = z3.Repeat( z3.Then("propagate-values", "propagate-ineqs", "unit-subsume-simplify", "propagate-bv-bounds", "simplify")) t_qe = z3.OrElse(z3.Then(z3.TryFor('qe', 1000), z3.TryFor('qe2', 1000)), 'skip') def gen_smt_expr(ast): if isinstance(ast, pycp.c_ast.Constant): return z3.BitVecVal(ast.value, 32) elif isinstance(ast, pycp.c_ast.ID): return vars[ast.name] elif isinstance(ast, pycp.c_ast.UnaryOp): if ast.op == "-": return "-" + gen_smt_expr(ast.expr) if ast.op == "!": return z3.Not(gen_smt_expr(ast.expr)) elif isinstance(ast, pycp.c_ast.BinaryOp): lexp = gen_smt_expr(ast.left) rexp = gen_smt_expr(ast.right) if ast.op == "+": return lexp + rexp if ast.op == "-": return lexp - rexp
def solve(threshold): MAX_TURN = 42 s = z3.Goal() # z3.Solver() bb_player = [z3.BitVec(f"bb_player_{i}", 64) for i in range(MAX_TURN + 2)] bb_opponent = [ z3.BitVec(f"bb_opponent_{i}", 64) for i in range(MAX_TURN + 2) ] pop_move = [z3.BitVec(f"pop_move_{i}", 64) for i in range(MAX_TURN + 2)] directions1 = [z3.BitVec(f"directions1_{i}", 64) for i in range(4)] directions2 = [z3.BitVec(f"directions2_{i}", 64) for i in range(4)] flip_const1 = [z3.BitVec(f"flip_const1_{i}", 64) for i in range(4)] flip_const2 = [z3.BitVec(f"flip_const2_{i}", 64) for i in range(4)] flip_const3 = [z3.BitVec(f"flip_const3_{i}", 64) for i in range(4)] num_zero = z3.BitVec("num_zero", 64) num_one = z3.BitVec("num_one", 64) num_threshold = z3.BitVec("num_threshold", 64) s.add(bb_player[0] == 0x0000001008000000, bb_opponent[0] == 0x0000000810000000, pop_move[0] == 0, directions1[0] == 1, directions1[1] == 7, directions1[2] == 8, directions1[3] == 9, directions2[0] == 2, directions2[1] == 14, directions2[2] == 16, directions2[3] == 18, flip_const1[0] == 0xFFFFFFFFFFFFFFFF, flip_const1[1] == 0x7E7E7E7E7E7E7E7E, flip_const1[2] == 0x7E7E7E7E7E7E7E7E, flip_const1[3] == 0x7E7E7E7E7E7E7E7E, flip_const2[0] == 0x0080808080808080, flip_const2[1] == 0x7F00000000000000, flip_const2[2] == 0x0102040810204000, flip_const2[3] == 0x0040201008040201, flip_const3[0] == 0x0101010101010100, flip_const3[1] == 0x00000000000000FE, flip_const3[2] == 0x0002040810204080, flip_const3[3] == 0x8040201008040200, num_zero == 0, num_one == 1, num_threshold == threshold) for turn in range(MAX_TURN + 1): bb_occupied = z3.BitVec(f"bb_occupied_{turn}", 64) bb_empty = z3.BitVec(f"bb_empty_{turn}", 64) masked_bb_opponent = z3.BitVec(f"masked_bb_opponent_{turn}", 64) movemask = [z3.BitVec(f"movemask_{turn}_{i}", 64) for i in range(4)] flip_l_0 = [z3.BitVec(f"flip_l_0_{turn}_{i}", 64) for i in range(4)] flip_r_0 = [z3.BitVec(f"flip_r_0_{turn}_{i}", 64) for i in range(4)] flip_l_1 = [z3.BitVec(f"flip_l_1_{turn}_{i}", 64) for i in range(4)] flip_r_1 = [z3.BitVec(f"flip_r_1_{turn}_{i}", 64) for i in range(4)] flip_l_2 = [z3.BitVec(f"flip_l_2_{turn}_{i}", 64) for i in range(4)] flip_r_2 = [z3.BitVec(f"flip_r_2_{turn}_{i}", 64) for i in range(4)] flip_l_3 = [z3.BitVec(f"flip_l_3_{turn}_{i}", 64) for i in range(4)] flip_r_3 = [z3.BitVec(f"flip_r_3_{turn}_{i}", 64) for i in range(4)] mask_l = [z3.BitVec(f"mask_l_{turn}_{i}", 64) for i in range(4)] mask_r = [z3.BitVec(f"mask_r_{turn}_{i}", 64) for i in range(4)] some_moves = [ z3.BitVec(f"some_moves_{turn}_{i}", 64) for i in range(4) ] all_moves = z3.BitVec(f"all_moves_{turn}", 64) pop_now = z3.BitVec(f"pop_now_{turn}", 64) popcnt_move_tmp1 = z3.BitVec(f"popcnt_move_tmp1_{turn}", 64) popcnt_move_tmp2 = z3.BitVec(f"popcnt_move_tmp2_{turn}", 64) s.add(bb_occupied == bb_player[turn] | bb_opponent[turn], bb_empty == bb_occupied ^ 0xFFFFFFFFFFFFFFFF, masked_bb_opponent == bb_opponent[turn] & 0x7E7E7E7E7E7E7E7E, movemask[0] == masked_bb_opponent, movemask[1] == masked_bb_opponent, movemask[2] == bb_opponent[turn], movemask[3] == masked_bb_opponent) s.add([ z3.And(flip_l_0[i] == (movemask[i] & (bb_player[turn] << directions1[i]))) for i in range(4) ]) s.add([ z3.And(flip_r_0[i] == (movemask[i] & z3.LShR(bb_player[turn], directions1[i]))) for i in range(4) ]) s.add([ z3.And(flip_l_1[i] == ( flip_l_0[i] | (movemask[i] & (flip_l_0[i] << directions1[i])))) for i in range(4) ]) s.add([ z3.And(flip_r_1[i] == ( flip_r_0[i] | (movemask[i] & z3.LShR(flip_r_0[i], directions1[i])))) for i in range(4) ]) s.add([ z3.And(mask_l[i] == (movemask[i] & (movemask[i] << directions1[i]))) for i in range(4) ]) s.add([ z3.And(mask_r[i] == (movemask[i] & z3.LShR(movemask[i], directions1[i]))) for i in range(4) ]) s.add([ z3.And(flip_l_2[i] == ( flip_l_1[i] | (mask_l[i] & (flip_l_1[i] << directions2[i])))) for i in range(4) ]) s.add([ z3.And(flip_r_2[i] == ( flip_r_1[i] | (mask_r[i] & z3.LShR(flip_r_1[i], directions2[i])))) for i in range(4) ]) s.add([ z3.And(flip_l_3[i] == ( flip_l_2[i] | (mask_l[i] & (flip_l_2[i] << directions2[i])))) for i in range(4) ]) s.add([ z3.And(flip_r_3[i] == ( flip_r_2[i] | (mask_r[i] & z3.LShR(flip_r_2[i], directions2[i])))) for i in range(4) ]) s.add([ z3.And(some_moves[i] == ((flip_l_3[i] << directions1[i]) | z3.LShR(flip_r_3[i], directions1[i]))) for i in range(4) ]) s.add( all_moves == (some_moves[0] | some_moves[1] | some_moves[2] | some_moves[3]) & bb_empty, popcnt_move_tmp1 == all_moves - (z3.LShR(all_moves, 1) & 0x7777777777777777) - (z3.LShR(all_moves, 2) & 0x3333333333333333) - (z3.LShR(all_moves, 3) & 0x1111111111111111), popcnt_move_tmp2 == ( (popcnt_move_tmp1 + z3.LShR(popcnt_move_tmp1, 4)) & 0x0F0F0F0F0F0F0F0F) * 0x0101010101010101, pop_now == z3.LShR( popcnt_move_tmp2, 56), pop_move[turn + 1] == z3.If(pop_now > pop_move[turn], pop_now, pop_move[turn])) move_onebit = z3.BitVec(f"move_onebit_{turn}", 64) move_bsf_tmp = [ z3.BitVec(f"move_bsf_tmp_{turn}_{i}", 64) for i in range(9) ] move_pos = z3.BitVec(f"move_pos_{turn}", 64) s.add( move_onebit & (move_onebit - 1) == 0, move_onebit & all_moves == move_onebit, move_bsf_tmp[0] == move_onebit - 1, move_bsf_tmp[1] == move_bsf_tmp[0] | z3.LShR(move_bsf_tmp[0], 1), move_bsf_tmp[2] == move_bsf_tmp[1] | z3.LShR(move_bsf_tmp[1], 2), move_bsf_tmp[3] == move_bsf_tmp[2] | z3.LShR(move_bsf_tmp[2], 4), move_bsf_tmp[4] == move_bsf_tmp[3] | z3.LShR(move_bsf_tmp[3], 8), move_bsf_tmp[5] == move_bsf_tmp[4] | z3.LShR(move_bsf_tmp[4], 16), move_bsf_tmp[6] == move_bsf_tmp[5] | z3.LShR(move_bsf_tmp[5], 32), move_bsf_tmp[7] == move_bsf_tmp[6] - (z3.LShR(move_bsf_tmp[6], 1) & 0x7777777777777777) - (z3.LShR(move_bsf_tmp[6], 2) & 0x3333333333333333) - (z3.LShR(move_bsf_tmp[6], 3) & 0x1111111111111111), move_bsf_tmp[8] == ((move_bsf_tmp[7] + z3.LShR(move_bsf_tmp[7], 4)) & 0x0F0F0F0F0F0F0F0F) * 0x0101010101010101, move_pos == z3.LShR(move_bsf_tmp[8], 56)) OM = [z3.BitVec(f"OM_{turn}_{i}", 64) for i in range(4)] mask1 = [z3.BitVec(f"mask1_{turn}_{i}", 64) for i in range(4)] upperbit_argument = [ z3.BitVec(f"upperbit_argument_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp1 = [ z3.BitVec(f"upperbit_tmp1_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp2 = [ z3.BitVec(f"upperbit_tmp2_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp3 = [ z3.BitVec(f"upperbit_tmp3_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp4 = [ z3.BitVec(f"upperbit_tmp4_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp5 = [ z3.BitVec(f"upperbit_tmp5_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp6 = [ z3.BitVec(f"upperbit_tmp6_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp7 = [ z3.BitVec(f"upperbit_tmp7_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp8 = [ z3.BitVec(f"upperbit_tmp8_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp9 = [ z3.BitVec(f"upperbit_tmp9_{turn}_{i}", 64) for i in range(4) ] upperbit_tmp10 = [ z3.BitVec(f"upperbit_tmp10_{turn}_{i}", 64) for i in range(4) ] upperbit_result = [ z3.BitVec(f"upperbit_result_{turn}_{i}", 64) for i in range(4) ] outflank1 = [z3.BitVec(f"outflank1_{turn}_{i}", 64) for i in range(4)] flipped1 = [z3.BitVec(f"flipped1_{turn}_{i}", 64) for i in range(4)] mask2 = [z3.BitVec(f"mask2_{turn}_{i}", 64) for i in range(4)] outflank2 = [z3.BitVec(f"outflank2_{turn}_{i}", 64) for i in range(4)] nonzero = [z3.BitVec(f"nonzero_{turn}_{i}", 64) for i in range(4)] flipped2 = [z3.BitVec(f"flipped2_{turn}_{i}", 64) for i in range(4)] bb_flip = z3.BitVec(f"bb_flip_{turn}", 64) next_player_tmp = z3.BitVec(f"next_player_tmp_{turn}", 64) next_opponent_tmp = z3.BitVec(f"next_opponent_tmp_{turn}", 64) s.add([ z3.And(OM[i] == (bb_opponent[turn] & flip_const1[i])) for i in range(4) ]) s.add([ z3.And(mask1[i] == z3.LShR(flip_const2[i], 63 - move_pos)) for i in range(4) ]) s.add([ z3.And(upperbit_argument[i] == (~OM[i]) & mask1[i]) for i in range(4) ]) s.add([ z3.And(upperbit_tmp1[i] == upperbit_argument[i] | z3.LShR(upperbit_argument[i], 1)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp2[i] == upperbit_tmp1[i] | z3.LShR(upperbit_tmp1[i], 2)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp3[i] == upperbit_tmp2[i] | z3.LShR(upperbit_tmp2[i], 4)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp4[i] == (~z3.LShR(upperbit_tmp3[i], 1)) & upperbit_tmp3[i]) for i in range(4) ]) s.add([ z3.And(upperbit_tmp5[i] == (upperbit_tmp4[i] << 32) | z3.LShR(upperbit_tmp4[i], 32)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp6[i] == ( (upperbit_tmp5[i] & 0x0000FFFF0000FFFF) << 16) | z3.LShR(upperbit_tmp5[i] & 0xFFFF0000FFFF0000, 16)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp7[i] == ( (upperbit_tmp6[i] & 0x00FF00FF00FF00FF) << 8) | z3.LShR(upperbit_tmp6[i] & 0xFF00FF00FF00FF00, 8)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp8[i] == upperbit_tmp7[i] & (-upperbit_tmp7[i])) for i in range(4) ]) s.add([ z3.And(upperbit_tmp9[i] == (upperbit_tmp8[i] << 32) | z3.LShR(upperbit_tmp8[i], 32)) for i in range(4) ]) s.add([ z3.And(upperbit_tmp10[i] == ( (upperbit_tmp9[i] & 0x0000FFFF0000FFFF) << 16) | z3.LShR(upperbit_tmp9[i] & 0xFFFF0000FFFF0000, 16)) for i in range(4) ]) s.add([ z3.And(upperbit_result[i] == ( (upperbit_tmp10[i] & 0x00FF00FF00FF00FF) << 8) | z3.LShR(upperbit_tmp10[i] & 0xFF00FF00FF00FF00, 8)) for i in range(4) ]) s.add([ z3.And(outflank1[i] == upperbit_result[i] & bb_player[turn]) for i in range(4) ]) s.add([ z3.And(flipped1[i] == ((-outflank1[i]) << 1) & mask1[i]) for i in range(4) ]) s.add( [z3.And(mask2[i] == flip_const3[i] << move_pos) for i in range(4)]) s.add([ z3.And(outflank2[i] == ((OM[i] | (~mask2[i])) + 1) & mask2[i] & bb_player[turn]) for i in range(4) ]) s.add([ z3.And(nonzero[i] == z3.If(outflank2[i] == 0, num_zero, num_one)) for i in range(4) ]) s.add([ z3.And(flipped2[i] == (outflank2[i] - nonzero[i]) & mask2[i]) for i in range(4) ]) s.add( bb_flip == flipped1[0] | flipped1[1] | flipped1[2] | flipped1[3] | flipped2[0] | flipped2[1] | flipped2[2] | flipped2[3], next_player_tmp == bb_opponent[turn] ^ bb_flip, next_opponent_tmp == bb_player[turn] ^ (bb_flip | move_onebit), bb_player[turn + 1] == z3.If(move_onebit == 0, bb_opponent[turn], next_player_tmp), bb_opponent[turn + 1] == z3.If(move_onebit == 0, bb_player[turn], next_opponent_tmp)) s.add(pop_move[MAX_TURN + 1] >= num_threshold) t = z3.Then('simplify', 'bit-blast', 'solve-eqs', 'tseitin-cnf') subgoal = t(s) assert len(subgoal) == 1 return subgoal[0]
def walk_block(node, prev_g=None, cond=True): g = z3.Goal() g.add(cond) if prev_g is not None: for e in prev_g: if isinstance(e, z3.Goal): g.add(e.as_expr()) else: g.add(e) if isinstance(node, pycp.c_ast.Compound): if node.block_items is not None: for e in node.block_items: g_next = walk_block(e, g) g = g_next elif isinstance(node, pycp.c_ast.Decl): if "int" in node.type.type.names: vars[node.name] = z3.Int(node.name) if "float" in node.type.type.names: vars[node.name] = z3.Real(node.name) elif isinstance(node, pycp.c_ast.FuncCall): if node.name.name == "__ASSUME": for e_exp in node.args.exprs: g.add(gen_smt_expr(e_exp)) elif node.name.name == "__ASSERT": assertions = z3.Goal() for e_exp in node.args.exprs: assertions.add(gen_smt_expr(e_exp)) print("solving..") print("SP:", g.as_expr()) print("assert:", assertions) seen = set() def bv_length(e): li = [-1] if e in seen: return -1 if (z3.is_bv(e) and z3.is_const(e) and e.decl().kind() == z3.Z3_OP_UNINTERPRETED): li.append(e.size()) seen.add(e) if z3.is_app(e): for ch in e.children(): li.append(bv_length(ch)) elif z3.is_quantifier(e): for ch in e.body().children(): li.append(bv_length(ch)) return max(li) t = z3.Tactic('nla2bv') s = z3.Then(t, 'default').solver() fml = z3.And(g.as_expr(), z3.Not(assertions.as_expr())) print("solving using bitvector underapproximation..") s.add(fml) status = s.check() if status == z3.unknown: print("returned 'unknown'! trying again with bigger bit length..") print("getting highest bit length used in formula..") bv_l = bv_length(t(fml).as_expr()) print("highest bit length used:", bv_l) while True: bv_l += 1 print("trying with bit length:", bv_l) s = z3.Then(z3.With('nla2bv', nla2bv_bv_size=bv_l), 'default').solver() s.add(fml) status = s.check() if status != z3.unknown or bv_l >= 64: break if status == z3.sat: model = s.model() print("program is unsafe.\nlisting an unsafe assignments..") for e in vars: print(e, ':', model[vars[e]]) elif status == z3.unsat: print("program is safe.") elif status == z3.unknown: print("unknown") s.reset() else: print("found a func call") elif isinstance(node, pycp.c_ast.Assignment): rexp = gen_smt_expr(node.rvalue) if z3.is_int(vars[node.lvalue.name]): hand_ = z3.Int('__hand__') elif z3.is_real(vars[node.lvalue.name]): hand_ = z3.Real('__hand__') if node.op == "=": g.add(hand_ == rexp) elif node.op == "+=": g.add(hand_ == (vars[node.lvalue.name] + rexp)) elif node.op == "-=": g.add(hand_ == (vars[node.lvalue.name] - rexp)) elif node.op == "*=": g.add(hand_ == (vars[node.lvalue.name] * rexp)) elif node.op == "%=": g.add(hand_ == (vars[node.lvalue.name] % rexp)) g_ = z3.Goal() g_.add(z3.Exists(vars[node.lvalue.name], g.as_expr())) g_ = t_qe_(g_) g = z3.Goal() g.add(z3.substitute(g_.as_expr(), (hand_, vars[node.lvalue.name]))) # g = g.simplify() elif isinstance(node, pycp.c_ast.If): cond_exp = gen_smt_expr(node.cond) if node.iftrue is not None: true_expr = walk_block(node.iftrue, g, cond_exp).as_expr() else: true_expr = z3.And(cond_exp, g.as_expr()) if node.iffalse is not None: false_expr = walk_block( node.iffalse, g, z3.Not(cond_exp)).as_expr() else: false_expr = z3.And(z3.Not(cond_exp), g.as_expr()) g = z3.Goal() g.add(z3.Or(true_expr, false_expr)) g = t(g) # g.simplify() else: return prev_g # print(g.as_expr(), "\n") return g
#!/usr/bin/python3 import pycparser as pycp import z3 import sys t_qe = z3.Tactic('qe') t = z3.Repeat(z3.Then("simplify", "propagate-ineqs", "propagate-values", "unit-subsume-simplify", z3.OrElse("split-clause", "skip"))) t_qe_ = z3.Then(t_qe, t) def gen_smt_expr(ast): if isinstance(ast, pycp.c_ast.Constant): return z3.IntVal(ast.value) elif isinstance(ast, pycp.c_ast.ID): return vars[ast.name] elif isinstance(ast, pycp.c_ast.UnaryOp): if ast.op == "-": return "-" + gen_smt_expr(ast.expr) if ast.op == "!": return z3.Not(gen_smt_expr(ast.expr)) elif isinstance(ast, pycp.c_ast.BinaryOp): lexp = gen_smt_expr(ast.left) rexp = gen_smt_expr(ast.right) if ast.op == "+": return lexp + rexp if ast.op == "-": return lexp - rexp elif ast.op == "*":
def write(self): t = z3.Then('simplify', 'bit-blast', 'tseitin-cnf') subgoal = t(self.slvr) assert len(subgoal) == 1 open('cnf.dimacs', 'w').write(subgoal[0].dimacs())
def check_equivalence(prog_before, prog_after, allow_undef): # The equivalence check of the solver # For all input packets and possible table matches the programs should # be the same try: # the equivalence equation log.debug("Simplifying equation...") tv_equiv = z3.simplify(prog_before != prog_after) except z3.Z3Exception as e: prog_before_simpl = z3.simplify(prog_before) prog_after_simpl = z3.simplify(prog_after) log.error("Failed to compare z3 formulas!\nReason: %s", e) log.error("PROGRAM BEFORE\n%s", prog_before_simpl) log.error("PROGRAM AFTER\n%s", prog_after_simpl) return util.EXIT_VIOLATION log.debug("Checking...") log.debug(z3.tactics()) t = z3.Then( z3.Tactic("simplify"), # z3.Tactic("distribute-forall"), # z3.Tactic("ackermannize_bv"), # z3.Tactic("bvarray2uf"), # z3.Tactic("card2bv"), # z3.Tactic("propagate-bv-bounds-new"), # z3.Tactic("reduce-bv-size"), # z3.Tactic("qe_rec"), z3.Tactic("smt"), ) s = t.solver() log.debug(s.sexpr()) ret = s.check(tv_equiv) log.debug(tv_equiv) log.debug(ret) if allow_undef and ret == z3.sat: prog_before = z3.simplify(prog_before) prog_after = z3.simplify(prog_after) log.info("Detected difference in undefined behavior. " "Rechecking with undefined variables ignored.") taints = set() log.info("Preprocessing...") prog_before, _ = substitute_taint(prog_before, taints) log.info("Checking...") tv_equiv = prog_before != prog_after if taints: tv_equiv = z3.ForAll(list(taints), tv_equiv) # check equivalence of the modified clause ret = s.check(tv_equiv) if ret == z3.sat: prog_before_simpl = z3.simplify(prog_before) prog_after_simpl = z3.simplify(prog_after) log.error("Detected an equivalence violation!") log.error("PROGRAM BEFORE\n%s", prog_before_simpl) log.error("PROGRAM AFTER\n%s", prog_after_simpl) log.error("Proposed solution:") log.error(s.model()) return util.EXIT_VIOLATION elif ret == z3.unknown: prog_before_simpl = z3.simplify(prog_before) prog_after_simpl = z3.simplify(prog_after) log.error("Solution unknown! There might be a problem...") return util.EXIT_VIOLATION else: return util.EXIT_SUCCESS
def perform_blackbox_test(config): out_dir = config["out_dir"] p4_input = config["p4_input"] if out_dir == OUT_DIR: out_dir = out_dir.joinpath(p4_input.stem) util.check_dir(out_dir) util.copy_file(p4_input, out_dir) config["out_dir"] = out_dir config["p4_input"] = p4_input # get the semantic representation of the original program z3_main_prog, result = get_semantics(config) if result != util.EXIT_SUCCESS: return result # now we actually verify that we can find an input s = z3.Solver() # we currently ignore all other pipelines and focus on the ingress pipeline main_formula = z3.simplify(z3_main_prog[config["pipe_name"]]) # this util might come in handy later. # z3.z3util.get_vars(main_formula) conditions = get_branch_conditions(main_formula) log.info(conditions) cond_tuple = dissect_conds(config, conditions) permut_conds, avoid_conds, undefined_conds = cond_tuple log.info("Computing permutations...") # FIXME: This does not scale well... # FIXME: This is a complete hack, use a more sophisticated method permuts = [[f(var) for var, f in zip(permut_conds, x)] for x in itertools.product([z3.Not, lambda x: x], repeat=len(permut_conds))] output_const = z3.Const("output", main_formula.sort()) # bind the output constant to the output of the main program s.add(main_formula == output_const) # all keys must be false for now # FIXME: Some of them should be usable log.info(15 * "#") log.info("Undefined conditions:") s.add(z3.And(*undefined_conds)) for cond in undefined_conds: log.info(cond) log.info("Conditions to avoid:") s.add(z3.Not(z3.Or(*avoid_conds))) for cond in avoid_conds: log.info(cond) log.info("Permissible permutations:") for cond in permuts: log.info(cond) log.info(15 * "#") for permut in permuts: s.push() s.add(permut) log.info("Checking for solution...") ret = s.check() if ret == z3.sat: log.info("Found a solution!") # get the model m = s.model() # this does not work well yet... desperate hack # FIXME: Figure out a way to solve this, might not be solvable avoid_matches = z3.Not(z3.Or(*avoid_conds)) undefined_matches = z3.And(*undefined_conds) permut_match = z3.And(*permut) g = z3.Goal() g.add(main_formula == output_const, avoid_matches, undefined_matches, permut_match) log.debug(z3.tactics()) t = z3.Then(z3.Tactic("propagate-values"), z3.Tactic("ctx-solver-simplify"), z3.Tactic("elim-and")) log.info("Inferring simplified input and output") constrained_output = t.apply(g) log.info("Inferring dont-care map...") output_var = constrained_output[0][0] dont_care_map = get_dont_care_map(config, output_var) result = check_with_stf(config, m, output_const, dont_care_map) if result != util.EXIT_SUCCESS: return result else: # FIXME: This should be an error log.warning("No valid input could be found!") s.pop() return result