def to_smt_expr_under_op (expr, env, solv): if expr.kind == 'Op': vals = [to_smt_expr (v, env, solv) for v in expr.vals] return syntax.Expr ('Op', expr.typ, name = expr.name, vals = vals) else: return to_smt_expr (expr, env, solv)
def get_var (k): head = rep.p.loop_id (i) 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))
def trace_mem(rep, tag, m, verbose=False, simplify=True, symbs=True, resolve_addrs=False): p = rep.p ns = walk_model(rep, tag, m) trace = [] for (n, vc) in ns: if (n, vc) not in rep.arc_pc_envs: # this n_vc has a pre-state, but has not been emitted. # no point trying to evaluate its expressions, the # solve won't have seen them yet. continue n_nm = rep.node_count_name((n, vc)) node = p.nodes[n] if node.kind == 'Call': exprs = list(node.args) elif node.kind == 'Basic': exprs = [expr for (_, expr) in node.upds] elif node.kind == 'Cond': exprs = [node.cond] env = rep.node_pc_envs[(tag, n, vc)][1] accs = list( set([acc for expr in exprs for acc in expr.get_mem_accesses()])) for (kind, addr, v, mem) in accs: addr_s = solver.smt_expr(addr, env, rep.solv) v_s = solver.smt_expr(v, env, rep.solv) addr = eval_str(addr, env, rep.solv, m) v = eval_str(v, env, rep.solv, m) m_nm = m_var_name(mem) print '%s: %s @ <%s> -- %s -- %s' % (kind, m_nm, addr, v, n_nm) if simplify: addr_s = simplify_sexp(addr_s, rep, m) v_s = simplify_sexp(v_s, rep, m) if verbose: print '\t %s -- %s' % (addr_s, v_s) if symbs: addr_n = str_to_num(addr) (hit_symbs, secs) = find_symbol(addr_n, output=False) ss = hit_symbs + secs if ss: print '\t [%s]' % ', '.join(ss) if resolve_addrs: accs = [(kind, solver.to_smt_expr(addr, env, rep.solv), solver.to_smt_expr(v, env, rep.solv), mem) for (kind, addr, v, mem) in accs] trace.extend([(kind, addr, v, mem, n, vc) for (kind, addr, v, mem) in accs]) if node.kind == 'Call': msg = '<function call to %s at %s>' % (node.fname, n_nm) print msg trace.append(msg) return trace
def trace_mem(rep, tag, m, verbose=False, simplify=True, symbs=True, resolve_addrs=False): p = rep.p ns = walk_model(rep, tag, m) trace = [] for (n, vc) in ns: if (n, vc) not in rep.arc_pc_envs: # this n_vc has a pre-state, but has not been emitted. # no point trying to evaluate its expressions, the # solve won't have seen them yet. continue n_nm = rep.node_count_name((n, vc)) node = p.nodes[n] if node.kind == "Call": msg = "<function call to %s at %s>" % (node.fname, n_nm) print msg trace.append(msg) if node.kind == "Basic": exprs = [expr for (_, expr) in node.upds] elif node.kind == "Cond": exprs = [node.cond] else: continue env = rep.node_pc_envs[(tag, n, vc)][1] accs = list(set([acc for expr in exprs for acc in expr.get_mem_accesses()])) for (kind, addr, v, mem) in accs: addr_s = solver.smt_expr(addr, env, rep.solv) v_s = solver.smt_expr(v, env, rep.solv) addr = eval_str(addr, env, rep.solv, m) v = eval_str(v, env, rep.solv, m) m_nm = m_var_name(mem) print "%s: %s @ <%s> -- %s -- %s" % (kind, m_nm, addr, v, n_nm) if simplify: addr_s = simplify_sexp(addr_s, rep, m) v_s = simplify_sexp(v_s, rep, m) if verbose: print "\t %s -- %s" % (addr_s, v_s) if symbs: (hit_symbs, secs) = find_symbol(addr, output=False) ss = hit_symbs + secs if ss: print "\t [%s]" % ", ".join(ss) if resolve_addrs: accs = [ (kind, solver.to_smt_expr(addr, env, rep.solv), solver.to_smt_expr(v, env, rep.solv), mem) for (kind, addr, v, mem) in accs ] trace.extend([(kind, addr, v, mem, n, vc) for (kind, addr, v, mem) in accs]) return trace
def eval_str(expr, env, solv, m): expr = solver.to_smt_expr(expr, env, solv) v = search.eval_model_expr(m, solv, expr) if v.typ == syntax.boolT: assert v in [syntax.true_term, syntax.false_term] return v.name elif v.typ.kind == "Word": return solver.smt_num(v.val, v.typ.num) else: assert not "type printable", v
def eval_str(expr, env, solv, m): expr = solver.to_smt_expr(expr, env, solv) v = search.eval_model_expr(m, solv, expr) if v.typ == syntax.boolT: assert v in [syntax.true_term, syntax.false_term] return v.name elif v.typ.kind == 'Word': return solver.smt_num_t(v.val, v.typ) else: assert not 'type printable', v
def find_case_split (p, head, restrs, hyps, tags = None): # are there multiple paths to the loop head 'head' and can we # restrict to one of them? preds = set () frontier = list (set ([n2 for n in p.loop_body (head) for n2 in p.preds[n] if p.loop_id (n2) != head])) while frontier: n2 = frontier.pop () if n2 in preds: continue preds.add (n2) frontier.extend (p.preds[n2]) divs = [n2 for n2 in preds if len (set ([n3 for n3 in p.nodes[n2].get_conts () if n3 in preds or n3 == head])) > 1 if n2 not in p.loop_data] trace ('find_case_split: possible divs %s.' % divs) rep = mk_graph_slice (p) err_restrs = restr_others (p, restrs, 2) if tags: l_tag, r_tag = tags else: l_tag, r_tag = p.pairing.tags hvis_restrs = tuple ([(head, rep_graph.vc_num (0))]) + restrs lhyps = hyps + [rep_graph.pc_false_hyp ((('Err', err_restrs), r_tag)), rep_graph.pc_true_hyp (((head, hvis_restrs), p.node_tags[head][0]))] # for this to be a usable case split, both paths must be possible for div in divs: dhyps = lhyps + [rep_graph.pc_true_hyp (((div, restrs), p.node_tags[div][0]))] assert p.nodes[div].kind == 'Cond' (_, env) = rep.get_node_pc_env ((div, restrs)) c = to_smt_expr (p.nodes[div].cond, env, rep.solv) if (rep.test_hyp_whyps (c, dhyps) or rep.test_hyp_whyps (mk_not (c), dhyps)): continue trace ("attempting case split at %d" % div) sides = [n for n in p.nodes[div].get_conts () if n not in p.loop_data if p.preds[n] == [div]] if not sides: trace ("skipping case split, no notable succ") n = sides[0] return ('CaseSplit', (n, p.node_tags[n][0])) trace ('find_case_split: no case splits possible') return (None, ())
def ident_callables (fname, callees, idents): from solver import to_smt_expr, smt_expr from syntax import mk_not, mk_and, true_term auto_callables = dict ([((ident, f, true_term), True) for ident in idents.get (fname, [true_term]) for f in callees if f not in idents]) if not [f for f in callees if f in idents]: return auto_callables fun = functions[fname] p = fun.as_problem (problem.Problem, name = 'Target') check_ns = [(n, ident, cond) for n in p.nodes if p.nodes[n].kind == 'Call' if p.nodes[n].fname in idents for (ident, cond) in ident_conds (p.nodes[n].fname, idents)] p.do_analysis () assert check_ns rep = rep_graph.mk_graph_slice (p, fast = True) err_hyp = rep_graph.pc_false_hyp ((default_n_vc (p, 'Err'), 'Target')) callables = auto_callables nhyps = mk_not_callable_hyps (p) for (ident, cond) in ident_conds (fname, idents): renames = p.entry_exit_renames (tags = ['Target']) cond = syntax.rename_expr (cond, renames['Target_IN']) entry = p.get_entry ('Target') e_vis = ((entry, ()), 'Target') hyps = [err_hyp, rep_graph.eq_hyp ((cond, e_vis), (true_term, e_vis))] for (n, ident2, cond2) in check_ns: k = (ident, p.nodes[n].fname, ident2) (inp_env, _, _) = rep.get_func (default_n_vc (p, n)) pc = rep.get_pc (default_n_vc (p, n)) cond2 = to_smt_expr (cond2, inp_env, rep.solv) if rep.test_hyp_whyps (mk_not (mk_and (pc, cond2)), hyps + nhyps): callables[k] = False else: callables[k] = True return callables
def ident_callables(fname, callees, idents): from solver import to_smt_expr, smt_expr from syntax import mk_not, mk_and, true_term auto_callables = dict([((ident, f, true_term), True) for ident in idents.get(fname, [true_term]) for f in callees if f not in idents]) if not [f for f in callees if f in idents]: return auto_callables fun = functions[fname] p = fun.as_problem(problem.Problem, name='Target') check_ns = [(n, ident, cond) for n in p.nodes if p.nodes[n].kind == 'Call' if p.nodes[n].fname in idents for (ident, cond) in ident_conds(p.nodes[n].fname, idents)] p.do_analysis() assert check_ns rep = rep_graph.mk_graph_slice(p, fast=True) err_hyp = rep_graph.pc_false_hyp((default_n_vc(p, 'Err'), 'Target')) callables = auto_callables nhyps = mk_not_callable_hyps(p) for (ident, cond) in ident_conds(fname, idents): renames = p.entry_exit_renames(tags=['Target']) cond = syntax.rename_expr(cond, renames['Target_IN']) entry = p.get_entry('Target') e_vis = ((entry, ()), 'Target') hyps = [err_hyp, rep_graph.eq_hyp((cond, e_vis), (true_term, e_vis))] for (n, ident2, cond2) in check_ns: k = (ident, p.nodes[n].fname, ident2) (inp_env, _, _) = rep.get_func(default_n_vc(p, n)) pc = rep.get_pc(default_n_vc(p, n)) cond2 = to_smt_expr(cond2, inp_env, rep.solv) if rep.test_hyp_whyps(mk_not(mk_and(pc, cond2)), hyps + nhyps): callables[k] = False else: callables[k] = True return callables
def find_unknown_recursion (p, group, idents, tag, assns, extra_unfolds): from syntax import mk_not, mk_and, foldr1 rep = rep_graph.mk_graph_slice (p, fast = True) for n in p.nodes: if p.nodes[n].kind != 'Call': continue if p.node_tags[n][0] != tag: continue fname = p.nodes[n].fname if fname in extra_unfolds: return n if fname not in group: continue (inp_env, _, _) = rep.get_func (default_n_vc (p, n)) pc = rep.get_pc (default_n_vc (p, n)) new = foldr1 (mk_and, [pc] + [syntax.mk_not ( solver.to_smt_expr (ident, inp_env, rep.solv)) for ident in idents.get (fname, [])]) if rep.test_hyp_whyps (mk_not (new), assns): continue return n return None
def find_unknown_recursion(p, group, idents, tag, assns, extra_unfolds): from syntax import mk_not, mk_and, foldr1 rep = rep_graph.mk_graph_slice(p, fast=True) for n in p.nodes: if p.nodes[n].kind != 'Call': continue if p.node_tags[n][0] != tag: continue fname = p.nodes[n].fname if fname in extra_unfolds: return n if fname not in group: continue (inp_env, _, _) = rep.get_func(default_n_vc(p, n)) pc = rep.get_pc(default_n_vc(p, n)) new = foldr1(mk_and, [pc] + [ syntax.mk_not(solver.to_smt_expr(ident, inp_env, rep.solv)) for ident in idents.get(fname, []) ]) if rep.test_hyp_whyps(mk_not(new), assns): continue return n return None
def add_recursion_ident(f, group, idents, extra_unfolds): from syntax import mk_eq, mk_implies, mk_var p = problem.Problem(None, name='Recursion Test') chain = [] tag = 'fun0' p.add_entry_function(functions[f], tag) p.do_analysis() assns = [] recursion_last_assns[0] = assns while True: res = find_unknown_recursion(p, group, idents, tag, assns, extra_unfolds) if res == None: break if p.nodes[res].fname not in group: problem.inline_at_point(p, res) continue fname = p.nodes[res].fname chain.append(fname) tag = 'fun%d' % len(chain) (args, _, entry) = p.add_entry_function(functions[fname], tag) p.do_analysis() assns += function_link_assns(p, res, tag) if chain == []: return None recursion_trace.append(' created fun chain %s' % chain) word_args = [(i, mk_var(s, typ)) for (i, (s, typ)) in enumerate(args) if typ.kind == 'Word'] rep = rep_graph.mk_graph_slice(p, fast=True) (_, env) = rep.get_node_pc_env((entry, ())) m = {} res = rep.test_hyp_whyps(syntax.false_term, assns, model=m) assert m if find_unknown_recursion(p, group, idents, tag, [], []) == None: idents.setdefault(fname, []) idents[fname].append(syntax.true_term) recursion_trace.append(' found final ident for %s' % fname) return syntax.true_term assert word_args recursion_trace.append(' scanning for ident for %s' % fname) for (i, arg) in word_args: (nm, typ) = functions[fname].inputs[i] arg_smt = solver.to_smt_expr(arg, env, rep.solv) val = search.eval_model_expr(m, rep.solv, arg_smt) if not rep.test_hyp_whyps(mk_eq(arg_smt, val), assns): recursion_trace.append(' discarded %s = 0x%x, not stable' % (nm, val.val)) continue entry_vis = ((entry, ()), tag) ass = rep_graph.eq_hyp((arg, entry_vis), (val, entry_vis)) res = find_unknown_recursion(p, group, idents, tag, assns + [ass], []) if res: fname2 = p.nodes[res].fname recursion_trace.append( ' discarded %s, allows recursion to %s' % (nm, fname2)) continue eq = syntax.mk_eq(mk_var(nm, typ), val) idents.setdefault(fname, []) idents[fname].append(eq) recursion_trace.append(' found ident for %s: %s' % (fname, eq)) return eq assert not "identifying assertion found"
def add_recursion_ident (f, group, idents, extra_unfolds): from syntax import mk_eq, mk_implies, mk_var p = problem.Problem (None, name = 'Recursion Test') chain = [] tag = 'fun0' p.add_entry_function (functions[f], tag) p.do_analysis () assns = [] recursion_last_assns[0] = assns while True: res = find_unknown_recursion (p, group, idents, tag, assns, extra_unfolds) if res == None: break if p.nodes[res].fname not in group: problem.inline_at_point (p, res) continue fname = p.nodes[res].fname chain.append (fname) tag = 'fun%d' % len (chain) (args, _, entry) = p.add_entry_function (functions[fname], tag) p.do_analysis () assns += function_link_assns (p, res, tag) if chain == []: return None recursion_trace.append (' created fun chain %s' % chain) word_args = [(i, mk_var (s, typ)) for (i, (s, typ)) in enumerate (args) if typ.kind == 'Word'] rep = rep_graph.mk_graph_slice (p, fast = True) (_, env) = rep.get_node_pc_env ((entry, ())) m = {} res = rep.test_hyp_whyps (syntax.false_term, assns, model = m) assert m if find_unknown_recursion (p, group, idents, tag, [], []) == None: idents.setdefault (fname, []) idents[fname].append (syntax.true_term) recursion_trace.append (' found final ident for %s' % fname) return syntax.true_term assert word_args recursion_trace.append (' scanning for ident for %s' % fname) for (i, arg) in word_args: (nm, typ) = functions[fname].inputs[i] arg_smt = solver.to_smt_expr (arg, env, rep.solv) val = search.eval_model_expr (m, rep.solv, arg_smt) if not rep.test_hyp_whyps (mk_eq (arg_smt, val), assns): recursion_trace.append (' discarded %s = 0x%x, not stable' % (nm, val.val)) continue entry_vis = ((entry, ()), tag) ass = rep_graph.eq_hyp ((arg, entry_vis), (val, entry_vis)) res = find_unknown_recursion (p, group, idents, tag, assns + [ass], []) if res: fname2 = p.nodes[res].fname recursion_trace.append (' discarded %s, allows recursion to %s' % (nm, fname2)) continue eq = syntax.mk_eq (mk_var (nm, typ), val) idents.setdefault (fname, []) idents[fname].append (eq) recursion_trace.append (' found ident for %s: %s' % (fname, eq)) return eq assert not "identifying assertion found"
return self.get_loop_pc_env (split, vcount) pc_envs = [pc_env for n_prev in self.p.preds[n] if self.p.node_tags[n_prev][0] == tag for pc_env in self.get_arc_pc_envs (n_prev, (n, vcount))] pc_envs = [pc_env for pc_env in pc_envs if pc_env] if pc_envs == []: return None if n == 'Err': # we'll never care about variable values here # and there are sometimes a LOT of arcs to Err # so we save a lot of merge effort pc_envs = [(to_smt_expr (pc, env, self.solv), {}) for (pc, env) in pc_envs] (pc, env, large) = merge_envs_pcs (pc_envs, self.solv) if large or len (smt_expr (pc, env, self.solv)) > 80: name = self.path_cond_name ((n, vcount), tag) name = self.solv.add_def (name, pc, env) pc = mk_smt_expr (name, boolT) for (nm, typ) in env: if len (env[(nm, typ)]) > 80: env[(nm, typ)] = self.contract (nm, (n, vcount), env[(nm, typ)], typ) return (pc, env)