def check_model_and_resolve_inner(constraints, sha_constraints, second_try=False): # logging.debug('-' * 32) extra_constraints = [] s = z3.SolverFor("QF_ABV") z3.set_option(model_compress=False) s.add(constraints) if s.check() != z3.sat: raise IntractablePath("CHECK", "MODEL") else: if not sha_constraints: return s.model() while True: ne_constraints = [] for a, b in itertools.combinations(sha_constraints.keys(), 2): if (not isinstance(sha_constraints[a], SymRead) and not isinstance(sha_constraints[b], SymRead) and sha_constraints[a].size() != sha_constraints[b].size()): ne_constraints.append(a != b) continue s = z3.SolverFor("QF_ABV") z3.set_option(model_compress=False) s.add(constraints + ne_constraints + extra_constraints + [a != b, symread_neq(sha_constraints[a], sha_constraints[b])]) check_result = s.check() # logging.debug("Checking hashes %s and %s: %s", a, b, check_result) if check_result == z3.unsat: # logging.debug("Hashes MUST be equal: %s and %s", a, b) subst = [(a, b)] extra_constraints = [z3.simplify(z3.substitute(c, subst)) for c in extra_constraints] extra_constraints.append(symread_eq(symread_substitute(sha_constraints[a], subst), symread_substitute(sha_constraints[b], subst))) constraints = [z3.simplify(z3.substitute(c, subst)) for c in constraints] b_val = symread_substitute(sha_constraints[b], subst) sha_constraints = {z3.substitute(sha, subst): symread_substitute(sha_value, subst) for sha, sha_value in sha_constraints.items() if not sha is a or sha is b} sha_constraints[b] = b_val break else: # logging.debug("Hashes COULD be equal: %s and %s", a, b) pass else: break return check_and_model(constraints + extra_constraints, sha_constraints, ne_constraints, second_try=second_try)
def simulates_forwards(topo, a, b, field="vlan", edge_policy={}): """Determine if b simulates a up to field on one hop.""" p, pp = Consts("p pp", Packet) v, vv = Ints("v vv") # Z3 emits a warning about not finding a pattern for our quantification. # This is fine, so ignore it. set_option("WARNING", False) solv = Solver() solv.add(on_valid_port(topo, p)) # b doesn't need to forward packets on external links that don't satisfy the # ingress predicate edge_option = And(external_link(edge_policy, p), Not(edges_ingress(edge_policy, p))) solv.add( And( forwards(a, p, pp), ForAll([v, vv], Not(Or(forwards_with(b, p, {field: v}, pp, {field: vv}), edge_option)), patterns=[]), ) ) if solv.check() == unsat: set_option("WARNING", True) return None else: set_option("WARNING", True) return solv.model(), (p, pp), HEADER_INDEX
def simulates_forwards2(topo, a, b, field="vlan", edge_policy={}): """Determine if b simulates a up to field on two hop on topo.""" p, pp, q, qq = Consts("p pp q qq", Packet) v, vv, vvv = Ints("v vv, vvv") # Z3 emits a warning about not finding a pattern for our quantification. # This is fine, so ignore it. set_option("WARNING", False) solv = Solver() solv.add(on_valid_port(topo, p)) # b doesn't need to forward packets on external links that don't satisfy the # ingress predicate, but we only care about the first hop edge_option = And(external_link(edge_policy, p), Not(edges_ingress(edge_policy, p))) # This case breaks the And inside the ForAll because they're both False and # z3 can't handle that. # However, it does mean that they simulate each other, because neither # forwards packets, so just short-circuit. if forwards(b, p, pp) is False and forwards(b, q, qq) is False: return None c = And( forwards(a, p, pp), transfer(topo, pp, q), forwards(a, q, qq), ForAll( [v, vv, vvv], Not( Or( And( forwards_with(b, p, {field: v}, pp, {field: vv}), forwards_with(b, q, {field: vv}, qq, {field: vvv}), ), edge_option, ) ), ), ) solv.add(c) if solv.check() == unsat: set_option("WARNING", True) return None else: set_option("WARNING", True) return solv.model(), (p, pp), HEADER_INDEX
def simulates_observes(topo, a, b, field="vlan", edge_policy={}): p = Const("p", Packet) o, v = Ints("o v") # Z3 emits a warning about not finding a pattern for our quantification. # This is fine, so ignore it. set_option("WARNING", False) solv = Solver() solv.add(on_valid_port(topo, p)) # b doesn't need to observe packets on external links that don't satisfy the # ingress predicate edge_option = And(external_link(edge_policy, p), Not(edges_ingress(edge_policy, p))) solv.add(And(observes(a, p, o), ForAll([v], Not(Or(observes_with(b, p, {field: v}, o), edge_option)), patterns=[]))) if solv.check() == unsat: set_option("WARNING", True) return None else: set_option("WARNING", True) return solv.model(), (p), HEADER_INDEX
def check_and_model(constraints, sha_constraints, ne_constraints, second_try=False): # logging.debug(' ' * 16 + '-' * 16) unresolved = set(sha_constraints.keys()) sol = z3.SolverFor("QF_ABV") z3.set_option(model_compress=False) sol.add(ne_constraints) todo = constraints progress = True all_vars = dict() while progress: new_todo = [] progress = False for c in todo: all_vars[c] = get_vars_non_recursive(c, include_select=True, include_indices=False) if any(x in unresolved for x in all_vars[c]): new_todo.append(c) else: progress = True sol.add(c) unresolved_vars = set(v.get_id() for c in new_todo for v in all_vars[c]) | set(v.get_id() for v in unresolved) # logging.debug("Unresolved vars: %s", ','.join(map(str, unresolved_vars))) if sol.check() != z3.sat: raise IntractablePath() m = sol.model() unresolved_todo = list(set(unresolved)) while unresolved_todo: u = unresolved_todo.pop() c = sha_constraints[u] if isinstance(c, SymRead): vars = set() if not concrete(c.start): vars |= get_vars_non_recursive(c.start, include_select=True) if not concrete(c.size): vars |= get_vars_non_recursive(c.size, include_select=True) # logging.debug("Trying to resolve %s, start and size vars: %s", u, ','.join(map(str, vars))) if any(x.get_id() in unresolved_vars for x in vars): continue start = c.start if not concrete(c.start): tmp = m.eval(c.start) if not z3util.is_expr_val(tmp): continue start = tmp.as_long() sol.add(c.start == start) size = c.size if not concrete(c.size): tmp = m.eval(c.size) if not z3util.is_expr_val(tmp): continue size = tmp.as_long() sol.add(c.size == size) data = c.memory.read(start, size) if isinstance(data, list): if len(data) > 1: data = z3.Concat(*data) elif len(data) == 1: data = data[0] else: raise IntractablePath() sha_constraints = dict(sha_constraints) sha_constraints[u] = data unresolved_todo.append(u) else: vars = get_vars_non_recursive(c, include_select=True) # logging.debug("Trying to resolve %s, vars: %s", u, ','.join(map(str, vars))) if any(x.get_id() in unresolved_vars for x in vars): continue v = m.eval(c) if z3util.is_expr_val(v): sha = big_endian_to_int(sha3(to_bytes(v))) sol.add(c == v) sol.add(u == sha) unresolved.remove(u) progress = True todo = new_todo if sol.check() != z3.sat: raise IntractablePath() if todo: if second_try: raise IntractablePath() raise UnresolvedConstraints(unresolved) return sol.model()
################################################################################ """Sat conversion and solving for netcore. ONLY CHECK FOR UNSAT UNLESS YOU'RE MARK No observations yet. """ from z3.z3 import And, Or, Not, Implies, Function, ForAll from z3.z3 import Const, Consts, Solver, unsat, set_option, Int, Ints from netcore import HEADERS import netcore as nc from util import fields_of_policy set_option(pull_nested_quantifiers=True) from sat_core import nary_or, nary_and from sat_core import HEADER_INDEX, Packet, switch, port, vlan from sat_core import forwards, forwards_with, observes, observes_with from sat_core import input, output, ingress, egress from sat_core import external_link, edges_ingress, on_valid_port from verification import disjoint_observations def transfer(topo, p_out, p_in): """Build constraint for moving p_out to p_in across an edge.""" options = [] for s1, s2 in topo.edges(): p1 = topo.node[s1]["ports"][s2] p2 = topo.node[s2]["ports"][s1]