def _firewallSendRules(self): p_0 = z3.Const('%s_firewall_send_p_0' % (self.fw), self.ctx.packet) p_1 = z3.Const('%s_firewall_send_p_1' % (self.fw), self.ctx.packet) n_0 = z3.Const('%s_firewall_send_n_0' % (self.fw), self.ctx.node) n_1 = z3.Const('%s_firewall_send_n_1' % (self.fw), self.ctx.node) t_0 = z3.Int('%s_firewall_send_t_0' % (self.fw)) t_1 = z3.Int('%s_firewall_send_t_1' % (self.fw)) t_2 = z3.Int('%s_firewall_send_t_2' % (self.fw)) self.acl_func = z3.Function('%s_acl_func' % (self.fw), self.ctx.packet, z3.BoolSort()) self.constraints.append(z3.ForAll([n_0, p_0, t_0], z3.Implies(self.ctx.send(self.fw, n_0, p_0, t_0), \ z3.Exists([n_1, t_1], \ z3.And(self.ctx.recv(n_1, self.fw, p_0, t_1),\ t_1 < t_0))))) self.constraints.append(z3.ForAll([n_0, p_0, t_0], z3.Implies(\ z3.And(self.ctx.send(self.fw, n_0, p_0, t_0), \ z3.Not(self.acl_func(p_0))), \ z3.Exists([n_1, p_1, t_1], \ z3.And(self.ctx.send(self.fw, n_1, p_1, t_1), \ t_1 + 1 <= t_0, \ self.acl_func(p_1), \ self.ctx.packet.src(p_0) == self.ctx.packet.dest(p_1), \ self.ctx.packet.dest(p_0) == self.ctx.packet.src(p_1), \ self.ctx.src_port(p_0) == self.ctx.dest_port(p_1), \ self.ctx.dest_port(p_0) == self.ctx.src_port(p_1), \ )))))
def _aclConstraints(self, solver): if len(self.acls) == 0: return #a_0 = z3.Const('%s_firewall_acl_a_0'%(self.fw), self.ctx.address) #a_1 = z3.Const('%s_firewall_acl_a_1'%(self.fw), self.ctx.address) p = z3.Const('%s_firewall_acl_p' % (self.fw), self.ctx.packet) conditions = [] for (a, b) in self.acls: a_part = None b_part = None if isinstance(a, str): a_part = self.sgroup.sgPredicate(a)(self.ctx.packet.src(p)) elif isinstance(a, bool): a_part = a else: assert (False) if isinstance(b, str): b_part = self.sgroup.sgPredicate(b)(self.ctx.packet.dest(p)) elif isinstance(b, bool): b_part = b else: assert (False) if isinstance(a, bool) and isinstance(b, bool): solver.add(z3.ForAll([p], self.acl_func(p) == (a and b))) return conditions.append(z3.And(a_part, b_part)) solver.add(z3.ForAll([p], self.acl_func(p) == z3.Or(conditions)))
def core_assertions(env): r = z3.Const('r', Unk) g = z3.Const('g', Unk) x = z3.Const('x', Unk) baseline = [] # r + () = r baseline.append( z3.ForAll([r], Z.Eq(Z.Concat(r, Unk._), r), patterns=[Z.Concat(r, Unk._)])) # () + r = r baseline.append( z3.ForAll([r], Z.Eq(Z.Concat(Unk._, r), r), patterns=[Z.Concat(Unk._, r)])) # f(g, *(x,), ...) = f(g, x, ...) baseline.append( z3.ForAll([x, g], Z.Eq(Z.Concat(g, Unk.app(Unk._, x)), Unk.app(g, x)), patterns=[Z.Concat(g, Unk.app(Unk._, x))])) # f(g, *(*r, x), ...) = f(g, *r, x, ...) baseline.append( z3.ForAll([x, g, r], Z.Eq(Z.Concat(g, Unk.app(r, x)), Unk.app(Z.Concat(g, r), x)), patterns=[Z.Concat(g, Unk.app(r, x))])) return baseline
def merge_queries_new_pred(clauses, queries, decls): if len(queries) > 1: false1 = z3.Bool("CHC_COMP_FALSE") decls.add(false1.decl()) for query in queries: assert (z3.is_quantifier(query) and query.is_forall()) qvars = [ z3.Const(query.var_name(n), query.var_sort(n)) for n in range(0, query.num_vars()) ] body = query.body() assert (body.decl().kind() == z3.Z3_OP_IMPLIES) kids = body.children() assert (kids[1] == z3.BoolVal(False)) newBody = z3.Implies(kids[0], false1) newClause = z3.ForAll(qvars, newBody) clauses.append(newClause) queries.clear() queries.append( z3.ForAll(z3.Bool("CHC_COMP_UNUSED"), z3.Implies(z3.And(false1), z3.BoolVal(False))))
def intremaps_equiv(conj, ctx, kernelstate): index = util.FreshBitVec('index', dt.size_t) # intremap index conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'state', index) == kernelstate.intremaps[index].state))) conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'devid', index) == kernelstate.intremaps[index].devid))) conj.append( z3.ForAll([index], z3.Implies( is_intremap_valid(index), util.global_field_element( ctx, '@intremap_table', 'vector', index) == kernelstate.intremaps[index].vector)))
def spec_lemma_nr_ports_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) port = util.FreshBitVec('port', dt.uint16_t) # unused procs don't own any ports # valid([port:pid]) ==> [port:pid].state != UNUSED conj.append( z3.ForAll([port], z3.Implies( is_pid_valid(kernelstate.io[port].owner), kernelstate.procs[kernelstate.io[port].owner].state != dt.proc_state.PROC_UNUSED))) # pid.state == UNUSED ==> pid.nr_ports == 0 conj.append( z3.ForAll( [pid], z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_ports() == z3.BitVecVal( 0, dt.size_t)))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_ports.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=lambda n: z3.BoolVal(True), max_refs=2**dt.uint16_t.size(), ownerfn=lambda port0: kernelstate.io[port0].owner) return z3.And(*conj)
def _populateLoadBalancerConstraints(self): self.hash_function = z3.Function( 'load_balancer_hash_%s' % (self.balancer), z3.IntSort(), z3.IntSort(), z3.IntSort()) p0 = z3.Const('_load_balancer_p0_%s' % (self.balancer), self.ctx.packet) p1 = z3.Const('_load_balancer_p1_%s' % (self.balancer), self.ctx.packet) n0 = z3.Const('_load_balancer_n0_%s' % (self.balancer), self.ctx.node) n1 = z3.Const('_load_balancer_n1_%s' % (self.balancer), self.ctx.node) n2 = z3.Const('_load_balancer_n2_%s' % (self.balancer), self.ctx.node) hash_same = [self.ctx.packet.src(p0) == self.ctx.packet.src(p1), \ self.ctx.packet.dest(p0) == self.ctx.packet.dest(p1), \ self.hash_function(self.ctx.src_port(p0), self.ctx.dest_port(p0)) == \ self.hash_function(self.ctx.src_port(p1), self.ctx.dest_port(p1)), \ self.ctx.send(self.balancer, n0, p0), \ self.ctx.send(self.balancer, n1, p1) ] self.constraints.append(z3.ForAll([n0, p0, n1, p1], \ z3.Implies(z3.And(hash_same), \ n0 == n1))) self.constraints.append(z3.ForAll([n0, p0], \ z3.Implies(\ self.ctx.send(self.balancer, n0, p0), \ z3.And( \ z3.Exists([n1], \ z3.And(self.ctx.recv(n1, self.balancer, p0), \ n1 != n0)), \ z3.Not(z3.Exists([n2], \ z3.And(n2 != n0, \ self.ctx.send(self.balancer, n2, p0)))), \ self.ctx.etime(self.balancer, p0, self.ctx.send_event) > \ self.ctx.etime(self.balancer, p0, self.ctx.recv_event)))))
def spec_lemma_nr_dmapages_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) dmapn = util.FreshBitVec('dmapn', dt.dmapn_t) # General invariant -- a free dma page has no owner conj.append( z3.ForAll([dmapn], z3.Implies( is_dmapn_valid(dmapn), is_pid_valid(kernelstate.dmapages[dmapn].owner) == ( kernelstate.dmapages[dmapn].type != dt.page_type.PAGE_TYPE_FREE)))) # PROC_UNUSED state implies refcount is 0 (sys_clone needs this) conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_dmapages() == z3.BitVecVal( 0, dt.size_t))))) # # Correctness definition for `permutation` based refcount kernelstate.procs.nr_dmapages.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_dmapn_valid, max_refs=dt.NDMAPAGE, ownerfn=lambda dmapn: kernelstate.dmapages[dmapn].owner) return z3.And(*conj)
def _constraints(self): #self.count_func = z3.Function('count_%s'%(self.node), self.ctx.address, self.ctx.address, \ #z3.IntSort(), z3.IntSort()) p0 = z3.Const('_counter_p0_%s' % (self.node), self.ctx.packet) p1 = z3.Const('_counter_p1_%s' % (self.node), self.ctx.packet) n0 = z3.Const('_counter_n0_%s' % (self.node), self.ctx.node) n1 = z3.Const('_counter_n1_%s' % (self.node), self.ctx.node) n2 = z3.Const('_counter_n2_%s' % (self.node), self.ctx.node) t0 = z3.Int('_counter_t0_%s' % (self.node)) t1 = z3.Int('_counter_t1_%s' % (self.node)) a0 = z3.Const('_counter_a0_%s' % (self.node), self.ctx.address) a1 = z3.Const('_counter_a1_%s' % (self.node), self.ctx.address) # Make sure all packets sent were first recved self.constraints.append(z3.ForAll([n0, p0], \ z3.Implies(self.ctx.send(self.node, n0, p0), \ z3.And( \ z3.Exists([n1], \ z3.And (self.ctx.recv(n1, self.node, p0), \ n0 != n1)), \ z3.Not(z3.Exists([n2], \ z3.And(self.ctx.send(self.node, n2, p0), \ n2 != n0))), \ self.ctx.etime(self.node, p0, self.ctx.send_event) > \ self.ctx.etime(self.node, p0, self.ctx.recv_event))))) # Make sure packets go one at a time self.constraints.append(z3.ForAll([p0, t0], \ z3.Implies(z3.And(self.ctx.etime(self.node, p0, self.ctx.send_event) == t0, \ t0 != 0), \ z3.ForAll([p1], \ z3.Or(p0 == p1, \ self.ctx.etime(self.node, p1, \ self.ctx.send_event) != \ t0)))))
def _addConstraints(self, solver): solver.add(self.constraints) p = z3.Const('__ips_acl_Packet_%s' % (self.ips), self.ctx.packet) addr_a = z3.Const('__ips_acl_cache_a_%s' % (self.ips), self.ctx.address) port_a = z3.Const('__ips_acl_port_a_%s' % (self.ips), z3.IntSort()) addr_b = z3.Const('__ips_acl_cache_b_%s' % (self.ips), self.ctx.address) port_b = z3.Const('__ips_acl_port_b_%s' % (self.ips), z3.IntSort()) aclConstraints = map(lambda (a, b): z3.And(self.ctx.packet.src(p) == a, \ self.ctx.packet.dest(p) == b), self.acls) eh = z3.Const('__ips_acl_node_%s' % (self.ips), self.ctx.node) # Constraints for what holes are punched # \forall a, b cached(a, b) \iff \exists e, p send(f, e, p) \land # p.src == a \land p.dest == b \land ctime(a, b) = etime(ips, p, R) \land # neg(ACL(p)) if len(aclConstraints) > 0: solver.add(z3.ForAll([addr_a, port_a, addr_b, port_b], self.cached(addr_a, port_a, addr_b, port_b) ==\ z3.Exists([eh, p], \ z3.And(self.ctx.recv(eh, self.ips, p), \ z3.And(self.ctx.packet.src (p) == addr_a, self.ctx.packet.dest(p) == addr_b, \ self.ctx.src_port (p) == port_a, self.ctx.dest_port (p) == port_b, \ self.ctime (addr_a, port_a, addr_b, port_b) == self.ctx.etime(self.ips, p, self.ctx.recv_event), \ z3.Not(z3.Or(aclConstraints))))))) else: solver.add(z3.ForAll([addr_a, port_a, addr_b, port_b], self.cached(addr_a, port_a, addr_b, port_b) ==\ z3.Exists([eh, p], \ z3.And(self.ctx.recv(eh, self.ips, p), \ z3.And(self.ctx.packet.src (p) == addr_a, self.ctx.packet.dest(p) == addr_b, \ self.ctx.src_port (p) == port_a, self.ctx.dest_port (p) == port_b, \ self.ctime (addr_a, port_a, addr_b, port_b) == self.ctx.etime(self.ips, p, self.ctx.recv_event))))))
def spec_lemma_nr_devs_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) devid = util.FreshBitVec('devid', dt.devid_t) # unused procs don't own any devices conj.append( z3.ForAll([devid], z3.Implies( is_pid_valid(kernelstate.pci[devid].owner), kernelstate.procs[kernelstate.pci[devid].owner].state != dt.proc_state.PROC_UNUSED))) conj.append( z3.ForAll( [pid], z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_devs() == z3.BitVecVal(0, dt.size_t)))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_devs.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=lambda n: z3.BoolVal(True), max_refs=2**dt.devid_t.size(), ownerfn=lambda devid0: kernelstate.pci[devid0].owner) return z3.And(*conj)
def _ipsFunctions(self): self.cached = z3.Function('__ips_cached_rules_%s' % (self.ips), self.ctx.address, z3.IntSort(), self.ctx.address, z3.IntSort(), z3.BoolSort()) self.ctime = z3.Function('__ips_cached_time_%s' % (self.ips), self.ctx.address, z3.IntSort(), self.ctx.address, z3.IntSort(), z3.IntSort()) addr_a = z3.Const('__ips_addr_cache_a_%s' % (self.ips), self.ctx.address) port_a = z3.Const('__ips_addr_port_a_%s' % (self.ips), z3.IntSort()) addr_b = z3.Const('__ips_addr_cache_b_%s' % (self.ips), self.ctx.address) port_b = z3.Const('__ips_addr_port_b_%s' % (self.ips), z3.IntSort()) self.constraints.append(z3.ForAll([addr_a, port_a, addr_b, port_b], z3.Implies(\ z3.Or(port_a < 0, \ port_a > Core.MAX_PORT, \ port_b < 0, \ port_a > Core.MAX_PORT), \ z3.Not(self.cached(addr_a, port_a, addr_b, port_b))))) self.constraints.append(z3.ForAll([addr_a, port_a, addr_b, port_b], self.ctime (addr_a, port_a, addr_b, port_b) \ >= 0)) self.constraints.append(z3.ForAll([addr_a, port_a, addr_b, port_b], z3.Implies(\ z3.Not(self.cached(addr_a, port_a, addr_b, port_b)), \ self.ctime (addr_a, port_a, addr_b, port_b) == 0)))
def _ipsSendRules(self): p = z3.Const('__ips_Packet_%s' % (self.ips), self.ctx.packet) eh = z3.Const('__ips_node1_%s' % (self.ips), self.ctx.node) eh2 = z3.Const('__ips_node2_%s' % (self.ips), self.ctx.node) eh3 = z3.Const('__ips_node3_%s' % (self.ips), self.ctx.node) # The ips never invents packets # \forall e_1, p\ send (f, e_1, p) \Rightarrow \exists e_2 recv(e_2, f, p) self.constraints.append(z3.ForAll([eh, p], z3.Implies(self.ctx.send(self.ips, eh, p), \ z3.Exists([eh2], \ z3.And(self.ctx.recv(eh2, self.ips, p), \ z3.Not(z3.Exists([eh3], z3.And(self.ctx.send(self.ips, eh3, p),\ eh3 != eh))), \ self.ctx.etime(self.ips, p, self.ctx.recv_event) < \ self.ctx.etime(self.ips, p, self.ctx.send_event)))))) # Actually enforce ips rules # \forall e_1, p send(f, e_1, p) \Rightarrow (cached(p.src, p.dest) # \land ctime(p.src, p.dest) <= etime(ips, p, R)) # \lor (cached(p.dest, p.src) \land ctime(p.dest, p.src) <= etime(ips. p, R)) self.constraints.append(z3.ForAll([eh, p], z3.Implies(self.ctx.send(self.ips, eh, p), \ z3.And(z3.Not(self.policy.dpi_match(self.ctx.packet.body(p))), \ z3.Or(z3.And(self.cached(self.ctx.packet.src(p), self.ctx.src_port(p), self.ctx.packet.dest(p), self.ctx.dest_port(p)), \ self.ctime(self.ctx.packet.src(p), self.ctx.src_port(p), self.ctx.packet.dest(p), self.ctx.dest_port(p)) <\ self.ctx.etime(self.ips, p, self.ctx.recv_event)), \ z3.And(self.cached(self.ctx.packet.dest(p), self.ctx.dest_port(p), self.ctx.packet.src(p), self.ctx.src_port(p)), \ self.ctime(self.ctx.packet.dest(p), self.ctx.dest_port(p), self.ctx.packet.src(p), self.ctx.src_port(p)) <\ self.ctx.etime(self.ips, p, self.ctx.recv_event)))))))
def pages_equiv(conj, ctx, kernelstate): pn = util.FreshBitVec('pn', dt.pn_t) idx = util.FreshBitVec('page_index', 64) conj.append( z3.ForAll([pn, idx], z3.Implies( z3.And(is_pn_valid(pn), z3.ULT(idx, 512)), util.global_to_uf_dict(ctx, '@pages')[()]( util.i64(0), pn, idx) == kernelstate.pages[pn].data(idx)))) conj.append( z3.ForAll( [pn], z3.Implies( is_pn_valid(pn), util.global_field_element(ctx, '@page_desc_table', 'pid', pn) == kernelstate.pages[pn].owner))) conj.append( z3.ForAll( [pn], z3.Implies( is_pn_valid(pn), util.global_field_element(ctx, '@page_desc_table', 'type', pn) == kernelstate.pages[pn].type)))
def setAddressMappings(self, addrmap): """Specify host to address mapping""" # Set address mapping for nodes. for node, addr in addrmap: a_0 = z3.Const('%s_address_mapping_a_0' % (node), self.ctx.address) # Node has a single address if not isinstance(addr, list) or len(addr) == 0: # $$addrToNode(addr) = node$$ self.constraints.append( self.ctx.addrToNode(addr) == node.z3Node) # $$nodeHasAddr(node, a_0) \iff a_0 = addr$$ # Note we need the $\iff$ here to make sure that we set nodeHasAddr to false # for other addresses. self.constraints.append(z3.ForAll([a_0], \ (a_0 == addr) == self.ctx.nodeHasAddr(node.z3Node, a_0))) # Node has several addresses else: or_clause = [] for ad in addr: # $$addrToNode(addr) = node$$ self.constraints.append( self.ctx.addrToNode(ad) == node.z3Node) or_clause.append(a_0 == ad) # Note we need the $\iff$ here to make sure that we set nodeHasAddr to false # for other addresses. self.constraints.append(z3.ForAll([a_0], \ z3.Or(or_clause) == self.ctx.nodeHasAddr(node.z3Node, a_0)))
def make_array(creator: SymbolicFactory) -> object: space = creator.space code, minval, maxval = pick_code(space) nums = SymbolicArrayBasedUniformTuple(creator.varname, Tuple[int, ...]) z3_array = nums._arr() qvar = z3.Int("arrvar" + space.uniq()) space.add(z3.ForAll([qvar], minval <= z3.Select(z3_array, qvar))) space.add(z3.ForAll([qvar], z3.Select(z3_array, qvar) < maxval)) return SymbolicArray(code, nums)
def impl_invariants_c(ctx): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) pn = util.FreshBitVec('pn', dt.pn_t) fd = util.FreshBitVec('fd', dt.fd_t) try: old_solver = ctx.solver old_globals = ctx.globals def implies(ctx, a, b): return z3.Implies(a, b) def and_(ctx, *args): return z3.And(*args) def or_(ctx, *args): return z3.Or(*args) ctx.globals['@implies'] = implies ctx.globals['@and2'] = and_ ctx.globals['@and3'] = and_ ctx.globals['@and4'] = and_ ctx.globals['@and5'] = and_ ctx.globals['@and6'] = and_ ctx.globals['@and7'] = and_ ctx.globals['@and8'] = and_ ctx.globals['@and9'] = and_ ctx.globals['@or2'] = or_ ctx.globals['@or3'] = or_ ctx.globals['@or4'] = or_ ctx.solver = solver.Solver() ctx.solver.add(z3.BoolVal(False)) conj.append(z3.ForAll([pid], ctx.call('@inv_proc_owns_pns', pid))) conj.append(z3.ForAll([pid], ctx.call( '@inv_sleeping_proc_owns_ipc', pid))) conj.append(z3.ForAll([pid], ctx.call( '@inv_sleeping_proc_ipc_fd_valid_empty', pid))) conj.append(z3.ForAll([pid], ctx.call('@inv_proc_pns_valid', pid))) conj.append(z3.ForAll([pid], ctx.call('@inv_io_bitmap', pid))) conj.append(z3.ForAll([pn], ctx.call('@inv_page_owner', pn))) conj.append(z3.ForAll([pn], ctx.call('@inv_proc_unused_refs', pn))) conj.append(z3.ForAll([pid, fd], ctx.call( '@inv_proc_fds_valid', pid, fd))) conj.append(z3.ForAll([pn], ctx.call('@inv_page_freelist_valid', pn))) conj.append(z3.ForAll([pid], ctx.call('@inv_proc_freelist_valid', pid))) conj.append(ctx.call('@inv_current_valid')) conj.append(ctx.call('@inv_current_running')) finally: ctx.solver = old_solver ctx.globals = old_globals return z3.And(*conj)
def spec_lemma_nr_children_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) # pid has a parent <=> pid is not PROC_UNUSED # valid(pid) & valid(ppid) ==> state(pid) != PROC_UNUSED conj.append( z3.ForAll([pid], z3.Implies( is_pid_valid(pid), is_pid_valid(kernelstate.procs[pid].ppid) == ( kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED)))) # valid(pid) & state(pid) == PROC_UNUSED ==> !valid(ppid) conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, z3.Not(is_pid_valid(kernelstate.procs[pid].ppid)))))) # PROC_UNUSED state implies refcnt is 0 # valid(pid) & state(pid) == PROC_UNUSED ==> pid.nr_children == 0 conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_children() == z3.BitVecVal( 0, dt.size_t))))) # unused procs don't have a parent conj.append( z3.ForAll( [pid], z3.Implies( z3.And( is_pid_valid(pid), kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED), kernelstate.procs[pid].ppid == z3.BitVecVal(0, dt.pid_t)))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_children.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_pid_valid, max_refs=dt.NPROC - 1, ownerfn=lambda pid0: kernelstate.procs[pid0].ppid) return z3.And(*conj)
def __eq__(self, other): ino = z3.Int(fresh_name('ino')) ino_match = z3.ForAll( [ino], z3.And(self.inode_nlink(ino) == other.inode_nlink(ino))) fname = z3.Int(fresh_name('fname')) fname_match = z3.ForAll( [fname], self.fname_inode(fname) == other.fname_inode(fname)) return z3.And(ino_match, fname_match)
def _add_column(self, numbers, skip, base, size, prefix): """Converts numbers in one line into assertions for self._solver. Args: numbers: A list of constraint numbers in one (either horizontal or vertical) line. skip, base, size: Specifies the line where the numbers come from. prefix: The name prefix of the new variables used in the assertion. Retunes: None. This provides set of assertions into self._solver. This is only expected to be called from the _solve() method.""" if numbers == []: # No numbers. All the field in this line should be False. for i in xrange(size): self._solver.add(z3.Not(self._field[base + skip * i])) return # Variables for the position of the sequence in the line. # If numbers = [4,6], v[0] is the index of the 4-length sequence, # v[1] is the index for the 6-length sequence. v = [z3.Int('%s%d' % (prefix, i)) for i in range(len(numbers))] # Assertions for v[]. # 0 <= v[0], v[0] + n[0] < v[1], ..., v[len-1] + n[len-1] <= size. self._solver.add(v[0] >= 0) for i in xrange(len(numbers) - 1): self._solver.add(v[i] + numbers[i] < v[i + 1]) self._solver.add(v[-1] + numbers[-1] <= size) # Assertions for filled fields. for s, c in zip(v, numbers): for i in xrange(c): self._solver.add(self._field[base + (s + i) * skip]) # Assertions for unfilled fields. fa = z3.Int('%s__1' % prefix) self._solver.add( z3.ForAll([fa], z3.Or(fa < 0, v[0] <= fa, z3.Not(self._field[base + skip * fa])))) for i in xrange(len(numbers) - 1): fa = z3.Int('%s_%d' % (prefix, i)) self._solver.add( z3.ForAll([fa], z3.Or(fa < v[i] + numbers[i], v[i + 1] <= fa, z3.Not(self._field[base + skip * fa])))) fa = z3.Int('%s_%d' % (prefix, len(numbers))) self._solver.add( z3.ForAll([fa], z3.Or(fa < v[-1] + numbers[-1], size <= fa, z3.Not(self._field[base + skip * fa]))))
def _idsSendRules(self): p_0 = z3.Const('%s_p_0' % (self.node), self.ctx.packet) n_0 = z3.Const('%s_n_0' % (self.node), self.ctx.node) n_1 = z3.Const('%s_n_1' % (self.node), self.ctx.node) t_0 = z3.Int('%s_t_0' % self.node) t_1 = z3.Int('%s_t_1' % self.node) self.constraints.append(z3.ForAll([n_0, p_0, t_0], z3.Implies(self.ctx.send(self.node, n_0, p_0, t_0), \ z3.Exists([n_1, t_1], \ z3.And(self.ctx.recv(n_1, self.node, p_0, t_1), \ t_1 < t_0))))) self.constraints.append(z3.ForAll([n_0, p_0, t_0], \ z3.Implies(z3.And(self.ctx.send(self.node, n_0, p_0, t_0), \ self.suspicious(p_0)), \ n_0 == self.shunt)))
def spec_lemma_nr_fds_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) fd = util.FreshBitVec('fd', dt.fd_t) # unused procs do not have any fds. # valid(pid) & state(pid)== PROC_UNUSED ==> pid.nr_fds == 0 conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_fds() == z3.BitVecVal( 0, dt.size_t))))) # unused procs do not have any opened files # valid(pid) & valid(fd) & state(pid) == PROC_UNUSED ==> !valid(openedfile(pid, fd)) conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd)), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, z3.Not(is_fn_valid(kernelstate.procs[pid].ofile(fd))))))) # a procs have opened a file, the state of this procs must not be UNUSED # valid(pid) & valid(fd) & valid(fn) ==> state(pid) != PROC_UNUSED conj.append( z3.ForAll( [pid, fd], z3.Implies( z3.And(is_pid_valid(pid), is_fd_valid(fd), is_fn_valid(kernelstate.procs[pid].ofile(fd))), kernelstate.procs[pid].state != dt.proc_state.PROC_UNUSED))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_fds.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_fd_valid, max_refs=dt.NOFILE, ownedby=lambda pid, fd: is_fn_valid(kernelstate.procs[pid].ofile(fd))) return z3.And(*conj)
def state_equiv(kernelstate, userstate): conj = [] pid = util.FreshBitVec('pid.eq', kdt.pid_t) idx1 = util.FreshBitVec('idx1.eq', kdt.size_t) idx2 = util.FreshBitVec('idx2.eq', kdt.size_t) idx3 = util.FreshBitVec('idx', kdt.size_t) idx4 = util.FreshBitVec('idx', kdt.size_t) conj.append(z3.ForAll([pid, idx1, idx2, idx3, idx4], z3.Implies( z3.And( kspec.is_pid_valid(pid), kspec.is_status_live(kernelstate.procs[pid].state), z3.ULT(idx1, 512), z3.ULT(idx2, 512), z3.ULT(idx3, 512), z3.ULT(idx4, 512), ), z3.And( pgwalk_rw(kernelstate, pid, idx1, idx2, idx3, idx4) == userstate.writable(pid, idx1, idx2, idx3, idx4) )))) return z3.And(*conj)
def test_successs_with_mock(self): x = z3.Int('pkt_0_0_0_0') simple_formula = z3.ForAll([x], z3.And(x > 3, x < 2)) pkt_fields, state_vars = z3_utils.generate_counterexamples( simple_formula) self.assertTrue('pkt_0' in pkt_fields) self.assertDictEqual(state_vars, {})
def test_state_group_with_alphabets(self): x = z3.Int('state_group_1_state_0_b_b_0') simple_formula = z3.ForAll([x], z3.And(x > 3, x < 2)) pkt_fields, state_vars = z3_utils.generate_counterexamples( simple_formula) self.assertDictEqual(pkt_fields, {}) self.assertTrue('state_group_1_state_0' in state_vars)
def check(*assertions, for_all=[], return_type='str'): ctx = Context() with munge_exceptions(): assertions = unwrap([assertion.eval(ctx) for assertion in assertions]) if for_all: for_all = unwrap([f.eval(ctx) for f in for_all]) assertions = [z3.ForAll(for_all, v) for v in assertions] SOLVER.reset() result = SOLVER.check(*assertions) if result != z3.sat: return (result, None) # The expression is satisfiable. Create a dictionary from the resulting # model, since the model returned by Z3 uses Z3 variables as keys, which # are only created on-demand in eval() above, thus not very useful outside # of this function model = SOLVER.model() if return_type == 'str': model = { v.name(): '%#0*x' % ((model[v].size() + 3) // 4 + 2, model[v].as_long()) for v in model } elif return_type == 'int': model = {v.name(): model[v].as_long() for v in model} elif return_type == 'var': model = {v.name(): model[v] for v in model} elif return_type.startswith('serial'): etype = return_type[7:] model = {v.name(): serialize(model[v], etype) for v in model} else: assert False, 'return type must be one of "str", "int", "var", or "serial:<type>"' return (result, model)
def _aclConstraints(self, solver): if len(self.acls) == 0: return a_0 = z3.Const('%s_firewall_acl_a_0'%(self.fw), self.ctx.address) a_1 = z3.Const('%s_firewall_acl_a_1'%(self.fw), self.ctx.address) acl_map = map(lambda (a, b): z3.Or(z3.And(a_0 == a, a_1 == b), z3.And(a_0 == b, a_1 == a)), self.acls) solver.add(z3.ForAll([a_0, a_1], self.acl_func(a_0, a_1) == z3.Or(acl_map)))
def fix_clause(clause): context = extract_implies(clause) # print 'working on' # print_context(context, pref=' ') qvars = context['qvars'] body_exprs = [] for expr in context['body']: # res = fix_pred_app(qvars, expr) # qvars = res[0] # body_exprs.append(res[1]) # for expr in res[2]: # body_exprs.append(expr) body_exprs.append(expr) res = fix_pred_app(qvars, context['head']) qvars = res[0] head = res[1] for expr in res[2]: body_exprs.append(expr) if len(body_exprs) > 1: body = z3.And(body_exprs) elif len(body_exprs) == 1: body = body_exprs[0] else: raise Exception('unexpected empty clause body') query = head.decl().kind() != z3.Z3_OP_UNINTERPRETED implies = z3.Implies(body, head) quantified = z3.ForAll(qvars, implies) # print(quantified) return (quantified, query)
def spec_lemma_nr_pages_refcnt(kernelstate): conj = [] pid = util.FreshBitVec('pid', dt.pid_t) # PROC_UNUSED state implies refcount is 0 (sys_clone needs this) conj.append( z3.ForAll( [pid], z3.Implies( is_pid_valid(pid), z3.Implies( kernelstate.procs[pid].state == dt.proc_state.PROC_UNUSED, kernelstate.procs[pid].nr_pages() == z3.BitVecVal( 0, dt.size_t))))) # Correctness definition for `permutation` based refcount kernelstate.procs.nr_pages.check( conj, is_owner_valid=is_pid_valid, is_owned_valid=is_pn_valid, max_refs=dt.NPAGE, ownerfn=lambda pn: kernelstate.pages[pn].owner) return z3.And(*conj)
def spec_corollary_pgwalk(kernelstate): pid = util.FreshBitVec('pid', dt.pid_t) va = dt.FreshVA() pml4 = kernelstate.procs[pid].page_table_root # Abstract model of a page_walk starting from some root page, writable, present = page_walk(kernelstate, pml4, *va) # If a four level page-walk from some pid returns a page, # that page must be exclusively owned by the pid. # furthermore, if the page is writable, # it has to be a frame. isolation = util.Implies( present, kernelstate.pages[page].owner == pid, z3.Implies( writable, kernelstate.pages[page].type == dt.page_type.PAGE_TYPE_FRAME)) # valid(pid) & pid.state == active & valid(va) ==> page.owner == pid && page.type == FRAME (active is either EMBRYO, RUNNABLE or RUNNING) return z3.ForAll([pid] + va, z3.Implies( z3.And( is_pid_valid(pid), is_status_live(kernelstate.procs[pid].state), is_va_valid(va), ), isolation))