def check_split_induct (p, restrs, hyps, split, tags = None): """perform both the induction check and a function-call based check on successes which can avoid some problematic inductions.""" ((l_split, (_, l_step), _), (r_split, (_, r_step), _), _, n, _) = split if tags == None: tags = p.pairing.tags err_hyp = check.split_r_err_pc_hyp (p, split, restrs, tags = tags) hyps = [err_hyp] + hyps + check.split_loop_hyps (tags, split, restrs, exit = False) rep = mk_graph_slice (p) if not check.check_split_induct_step_group (rep, restrs, hyps, split, tags = tags): return False l_succs = get_n_offset_successes (rep, l_split, l_step, restrs) r_succs = get_n_offset_successes (rep, r_split, r_step, restrs) if not l_succs: return True hyp = syntax.foldr1 (syntax.mk_and, l_succs) if r_succs: hyp = syntax.mk_implies (foldr1 (syntax.mk_and, r_succs), hyp) return rep.test_hyp_whyps (hyp, hyps)
def get_func_assert (self, n_vc, n_vc2): (pair, l_n_vc, r_n_vc) = self.get_func_pairing (n_vc, n_vc2) (ltag, rtag) = pair.tags (inp_eqs, out_eqs) = pair.eqs (lin, lout, lsucc) = self.funcs[l_n_vc] (rin, rout, rsucc) = self.funcs[r_n_vc] lpc = self.get_pc (l_n_vc) rpc = self.get_pc (r_n_vc) envs = {ltag + '_IN': lin, rtag + '_IN': rin, ltag + '_OUT': lout, rtag + '_OUT': rout} inp_eqs = inst_eqs (inp_eqs, envs, self.solv) out_eqs = inst_eqs (out_eqs, envs, self.solv) succ_imp = mk_implies (rsucc, lsucc) return mk_implies (foldr1 (mk_and, inp_eqs + [rpc]), foldr1 (mk_and, out_eqs + [succ_imp]))
def interpret (self, rep): if self.kind == 'PCImp': ((visit1, tag1), (visit2, tag2)) = self.pcs if visit1 == 'Bool': pc1 = tag1 else: pc1 = rep.get_pc (visit1, tag = tag1) if visit2 == 'Bool': pc2 = tag2 else: pc2 = rep.get_pc (visit2, tag = tag2) return mk_implies (pc1, pc2) elif self.kind == 'Eq': [(x, xvis), (y, yvis)] = self.vals if self.induct: v = rep.get_induct_var (self.induct) x = subst_induct (x, v) y = subst_induct (y, v) x_pc_env = rep.get_node_pc_env (xvis[0], tag = xvis[1]) y_pc_env = rep.get_node_pc_env (yvis[0], tag = yvis[1]) if x_pc_env == None or y_pc_env == None: return syntax.false_term ((_, xenv), (_, yenv)) = (x_pc_env, y_pc_env) return inst_eq_with_envs ((x, xenv), (y, yenv), rep.solv) else: assert not 'hypothesis type understood'
def find_split_limit (p, n, restrs, hyps, kind, bound = 51, must_find = True, hints = [], use_rep = None): tag = p.node_tags[n][0] trace ('Finding split limit: %d (%s) %s' % (n, tag, restrs)) trace (' (restrs = %s)' % (restrs, )) trace (' (hyps = %s)' % (hyps, ), push = 1) if use_rep == None: rep = mk_graph_slice (p, fast = True) else: rep = use_rep check_order = hints + [i for i in split_sample_set (bound) if i not in hints] for i in check_order: restrs2 = restrs + ((n, VisitCount (kind, i)), ) pc = rep.get_pc ((n, restrs2)) restrs3 = restr_others (p, restrs2, 2) epc = rep.get_pc (('Err', restrs3), tag = tag) hyp = mk_implies (mk_not (epc), mk_not (pc)) if rep.test_hyp_whyps (hyp, hyps): trace ('split limit found: %d' % i, push = -1) return i trace ('No split limit found for %d (%s).' % (n, tag), push = -1) if must_find: assert not 'split limit found' return None
def hyps_add_model (self, hyps, assert_progress = True): if hyps: test_expr = foldr1 (mk_and, hyps) else: # we want to learn something, either a new model, or # that all hyps are true. if there are no hyps, # learning they're all true is learning nothing. # instead force a model test_expr = false_term test_expr = mk_implies (self.premise, test_expr) m = {} (r, _) = self.rep.solv.parallel_check_hyps ([(1, test_expr)], {}, model = m) if r == 'unsat': if not hyps: trace ('WARNING: SearchKnowledge: premise unsat.') trace (" ... learning procedure isn't going to work.") return if assert_progress: assert not (set (hyps) <= self.facts), hyps for hyp in hyps: self.facts.add (hyp) else: assert r == 'sat', r self.add_model (m) if assert_progress: assert self.model_trace[-2:-1] != [m]
def get_n_offset_successes (rep, sp, step, restrs): loop = rep.p.loop_body (sp) ns = [n for n in loop if rep.p.nodes[n].kind == 'Call'] succs = [] for i in range (step): for n in ns: vc = vc_offs (i + 1) if n == sp: vc = vc_offs (i) n_vc = (n, restrs + tuple ([(sp, vc)])) (_, _, succ) = rep.get_func (n_vc) pc = rep.get_pc (n_vc) succs.append (syntax.mk_implies (pc, succ)) return succs
def tryLoopBound(p_n, p, bounds, rep, restrs=None, hints=None, kind='Number', bin_return=False, hyps=None): if restrs == None: restrs = () if hints == None: hints = [] if hyps == None: hyps = [] tag = p.node_tags[p_n][0] from stack_logic import default_n_vc print 'trying bound: %s' % bounds ret_bounds = [] for (index, i) in enumerate(bounds): print 'testing %d' % i restrs2 = restrs + ((p_n, VisitCount(kind, i)), ) try: pc = rep.get_pc((p_n, restrs2)) except: print 'get_pc failed' if bin_return: return False else: return -1 #print 'got rep_.get_pc' restrs3 = restr_others(p, restrs2, 2) epc = rep.get_pc(('Err', restrs3), tag=tag) hyp = mk_implies(mk_not(epc), mk_not(pc)) hyps = hyps + noHaltHyps(p_n, p) #hyps = [] #print 'calling test_hyp_whyps' if rep.test_hyp_whyps(hyp, hyps): print 'p_n %d: split limit found: %d' % (p_n, i) if bin_return: return True return index if bin_return: return False print 'loop bound not found!' return -1 assert False, 'failed to find loop bound for p_n %d' % p_n
def strengthen_hyp (expr, sign = 1): if not expr.kind == 'Op': return expr if expr.name in ['And', 'Or']: vals = [strengthen_hyp (v, sign) for v in expr.vals] return syntax.Expr ('Op', expr.typ, name = expr.name, vals = vals) elif expr.name == 'Implies': [l, r] = expr.vals l = strengthen_hyp (l, - sign) r = strengthen_hyp (r, sign) return syntax.mk_implies (l, r) elif expr.name == 'Not': [x] = expr.vals x = strengthen_hyp (x, - sign) return syntax.mk_not (x) elif expr.name == 'StackEquals': if sign == 1: return syntax.Expr ('Op', boolT, name = 'ImpliesStackEquals', vals = expr.vals) else: return syntax.Expr ('Op', boolT, name = 'StackEqualsImplies', vals = expr.vals) elif expr.name == 'ROData': if sign == 1: return syntax.Expr ('Op', boolT, name = 'ImpliesROData', vals = expr.vals) else: return expr elif expr.name == 'Equals' and expr.vals[0].typ == boolT: vals = expr.vals if vals[1] in [syntax.true_term, syntax.false_term]: vals = [vals[1], vals[0]] if vals[0] == syntax.true_term: return strengthen_hyp (vals[1], sign) elif vals[0] == syntax.false_term: return strengthen_hyp (syntax.mk_not (vals[1]), sign) else: return expr else: return expr
def tryLoopBound(p_n, p, bounds, rep, restrs=None, hints=None, kind="Number", bin_return=False, hyps=None): if restrs == None: restrs = () if hints == None: hints = [] if hyps == None: hyps = [] tag = p.node_tags[p_n][0] from stack_logic import default_n_vc print "trying bound: %s" % bounds ret_bounds = [] for (index, i) in enumerate(bounds): print "testing %d" % i restrs2 = restrs + ((p_n, VisitCount(kind, i)),) try: pc = rep.get_pc((p_n, restrs2)) except: print "get_pc failed" if bin_return: return False else: return -1 # print 'got rep_.get_pc' restrs3 = restr_others(p, restrs2, 2) epc = rep.get_pc(("Err", restrs3), tag=tag) hyp = mk_implies(mk_not(epc), mk_not(pc)) hyps = hyps + noHaltHyps(p_n, p) # hyps = [] # print 'calling test_hyp_whyps' if rep.test_hyp_whyps(hyp, hyps): print "p_n %d: split limit found: %d" % (p_n, i) if bin_return: return True return index if bin_return: return False print "loop bound not found!" return -1 assert False, "failed to find loop bound for p_n %d" % p_n
def add_model (knowledge, preds): (rep, (_, _, _, premise), _, facts) = knowledge if preds: pred_expr = foldr1 (mk_and, preds) else: # we want to learn something, either a new model, or that # all of our predicates are true. if there are no predicates, # ''all our predicates are true'' is trivially true. instead # we must want a model (counterexample of false) pred_expr = false_term m = {} r = rep.solv.check_hyp (mk_implies (premise, pred_expr), {}, model = m) if r == 'unsat': if not preds: trace ('WARNING: unsat with no extra assertions.') trace (" ... learning procedure isn't going to work.") for pred in preds: facts.add (pred) else: assert r == 'sat' update_knowledge_for_model (knowledge, m)
def get_loop_pc_env (self, split, vcount): vcount2 = dict (vcount) vcount2[split] = vc_num (0) vcount2 = tuple (sorted (vcount2.items ())) prev_pc_env = self.get_node_pc_env ((split, vcount2)) if prev_pc_env == None: return None (_, prev_env) = prev_pc_env def av (nm, typ, mem_name = None): nm2 = '%s_loop_at_%s' % (nm, split) return self.solv.add_var (nm2, typ, mem_name = mem_name) env = {} consts = set () for (nm, typ) in prev_env: check_const = self.fast or (typ in [builtinTs['HTD'], builtinTs['Dom']]) if check_const and self.is_synt_const (nm, typ, split): env[(nm, typ)] = prev_env[(nm, typ)] consts.add ((nm, typ)) else: env[(nm, typ)] = av (nm + '_after', typ, ('Loop', prev_env[(nm, typ)])) for (nm, typ) in prev_env: if (nm, typ) in consts: continue z = self.var_rep_request ((nm, typ), 'Loop', (split, vcount), env) if z: env[(nm, typ)] = z pc = mk_smt_expr (av ('pc_of', boolT), boolT) if self.fast: imp = syntax.mk_implies (pc, prev_pc_env[0]) self.solv.assert_fact (imp, prev_env, unsat_tag = ('LoopPCImp', split)) return (pc, env)
def interpret_hyp_imps (self, hyps, concl): imp = concl for h in hyps: imp = mk_implies (self.interpret_hyp (h), imp) return logic.strengthen_hyp (imp)
if i != head: k += 1 restrs2 = restrs + ((head, vc_num (k)), ) (pc, env) = rep.get_node_pc_env ((i, restrs2)) return (to_smt_expr (pc, env, rep.solv), to_smt_expr (v_i, env, rep.solv)) return [get_var (i_offs + (k * i_step)) for k in [0, 1, 2]] def expand_var_eqs (knowledge, (v_i, v_j)): (rep, (restrs, _, _, _), _, _) = knowledge if v_j == 'Const': pc_vs = get_var_pc_var_list (knowledge, v_i) (_, v0) = pc_vs[0] return [mk_implies (pc, mk_eq (v, v0)) for (pc, v) in pc_vs[1:]] # sorting the vars guarantees we generate the same # mem eqs each time which is important for the solver (v_i, v_j) = sorted ([v_i, v_j]) pc_vs = zip (get_var_pc_var_list (knowledge, v_i), get_var_pc_var_list (knowledge, v_j)) return [pred for ((pc_i, v_i), (pc_j, v_j)) in pc_vs for pred in [mk_eq (pc_i, pc_j), mk_implies (pc_i, logic.mk_eq_with_cast (v_i, v_j))]] word_ops = {'bvadd':lambda x, y: x + y, 'bvsub':lambda x, y: x - y, 'bvmul':lambda x, y: x * y, 'bvurem':lambda x, y: x % y, 'bvudiv':lambda x, y: x / y, 'bvand':lambda x, y: x & y, 'bvor':lambda x, y: x | y, 'bvxor': lambda x, y: x ^ y, 'bvnot': lambda x: ~ x, 'bvneg': lambda x: - x,
p = rep.p tag = p.node_tags[n][0] is_C = tag == 'C' or p.hook_tag_hints.get(tag, None) == 'C' if not is_C: return upd_ps = [ rep.to_smt_expr(ptr, (n, vc)) for (kind, ptr, v, m) in p.nodes[n].get_mem_accesses() if kind == 'MemUpdate' ] if not upd_ps: return cache = get_cache(p) for ptr in set(upd_ps): pc = rep.get_pc((n, vc)) eq_rodata = rep.solv.get_eq_rodata_witness(ptr) hyp = rep.to_smt_expr(syntax.mk_implies(pc, syntax.mk_not(eq_rodata)), (n, vc)) if ((n, vc), ptr) in cache: res = cache[((n, vc), ptr)] else: res = rep.test_hyp_whyps(hyp, [], cache=cache) cache[((n, vc), ptr)] = res if res: rep.solv.assert_fact(hyp, {}) module_hook_k = 'c_rodata' target_objects.add_hook('post_emit_node', module_hook_k, hook) target_objects.use_hooks.add(module_hook_k)