def inline_reachable_unmatched(p, inline_tag, compare_tag, force_inline=None, skip_underspec=False): funs = [ pair.funs[inline_tag] for n in p.nodes if p.nodes[n].kind == 'Call' if p.node_tags[n][0] == compare_tag for pair in pairings.get(p.nodes[n].fname, []) if inline_tag in pair.tags ] rep = mk_graph_slice( p, consider_inline(funs, inline_tag, force_inline, skip_underspec)) opts = vc_double_range(3, 3) while True: try: heads = problem.loop_heads_including_inner(p) limits = [(n, opts) for n in heads] for n in p.nodes.keys(): try: r = rep.get_node_pc_env((n, limits)) except rep.TooGeneral: pass rep.get_node_pc_env(('Ret', limits), inline_tag) rep.get_node_pc_env(('Err', limits), inline_tag) break except rep_graph.InlineEvent: continue
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 getBinaryBoundFromC(p, c_tag, asm_split, restrs, hyps): c_heads = [ h for h in search.init_loops_to_split(p, restrs) if p.node_tags[h][0] == c_tag ] c_bounds = [(p.loop_id(split), search_bound(p, (), hyps, split)) for split in c_heads] if not [b for (n, b) in c_bounds if b]: trace('no C bounds found (%s).' % c_bounds) return None asm_tag = p.node_tags[asm_split][0] rep = rep_graph.mk_graph_slice(p) i_seq_opts = [(0, 1), (1, 1), (2, 1)] j_seq_opts = [(0, 1), (0, 2), (1, 1)] tags = [p.node_tags[asm_split][0], c_tag] try: split = search.find_split(rep, asm_split, restrs, hyps, i_seq_opts, j_seq_opts, 5, tags=[asm_tag, c_tag]) except solver.SolverFailure, e: return None
def inline_reachable_unmatched (p, inline_tag, compare_tag, force_inline = None, skip_underspec = False): funs = [pair.funs['C'] for n in p.nodes if p.nodes[n].kind == 'Call' if p.node_tags[n][0] == compare_tag for pair in pairings.get (p.nodes[n].fname, []) if 'C' in pair.tags] rep = mk_graph_slice (p, consider_inline_c (funs, inline_tag, force_inline, skip_underspec)) opts = vc_double_range (3, 3) while True: try: limits = [(n, opts) for n in p.loop_heads ()] for n in p.nodes.keys (): try: r = rep.get_node_pc_env ((n, limits)) except rep.TooGeneral: pass rep.get_node_pc_env (('Ret', limits), inline_tag) rep.get_node_pc_env (('Err', limits), inline_tag) break except rep_graph.InlineEvent: continue
def search_bin_bound(p, restrs, hyps, split): trace('Searching for bound for 0x%x in %s.', (split, p.name)) bound = search_bound(p, restrs, hyps, split) if bound: return bound # try to use a bound inferred from C if avoid_C_information[0]: # OK told not to return None if get_prior_loop_heads(p, split): # too difficult for now return None asm_tag = p.node_tags[split][0] (_, fname, _) = p.get_entry_details(asm_tag) funs = [ f for pair in target_objects.pairings[fname] for f in pair.funs.values() ] c_tags = [ tag for tag in p.tags() if p.get_entry_details(tag)[1] in funs and tag != asm_tag ] if len(c_tags) != 1: print 'Surprised to see multiple matching tags %s' % c_tags return None [c_tag] = c_tags rep = rep_graph.mk_graph_slice(p) if len(search.get_loop_entry_sites(rep, restrs, hyps, split)) != 1: # technical, but it's not going to work in this case return None return getBinaryBoundFromC(p, c_tag, split, restrs, hyps)
def search_bound(p, restrs, hyps, split): last_search_bound[0] = (p, restrs, hyps, split) # try a naive bin search first # limit this to a small bound for time purposes # - for larger bounds the less naive approach can be faster bound = findLoopBoundBS(split, p, restrs=restrs, hyps=hyps, try_seq=[0, 1, 6]) if bound != None: return (bound, "NaiveBinSearch") l_hyps = get_linear_series_hyps(p, split, restrs, hyps) rep = rep_graph.mk_graph_slice(p, fast=True) def test(n): assert n > 10 hyp = get_induct_eq_hyp(p, split, restrs, n - 2) visit = ((split, vc_offs(2)),) + restrs continue_to_split_guess = rep.get_pc((split, visit)) return rep.test_hyp_whyps(syntax.mk_not(continue_to_split_guess), [hyp] + l_hyps + hyps) # findLoopBoundBS always checks to at least 16 min_bound = 16 max_bound = max_acceptable_bound[0] bound = upDownBinSearch(min_bound, max_bound, test) if bound != None and test(bound): return (bound, "InductiveBinSearch") # let the naive bin search go a bit further bound = findLoopBoundBS(split, p, restrs=restrs, hyps=hyps) if bound != None: return (bound, "NaiveBinSearch") return None
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 init_case_splits (p, hyps, tags = None): if 'init_case_splits' in p.cached_analysis: return p.cached_analysis['init_case_splits'] if tags == None: tags = p.pairing.tags poss = logic.possible_graph_divs (p) if len (set ([p.node_tags[n][0] for n in poss])) < 2: return None rep = rep_graph.mk_graph_slice (p) assert all ([p.nodes[n].kind == 'Cond' for n in poss]) pc_map = logic.dict_list ([(rep.get_pc ((c, ())), c) for n in poss for c in p.nodes[n].get_conts () if c not in p.loop_data]) no_loop_restrs = tuple ([(n, vc_num (0)) for n in p.loop_heads ()]) err_pc_hyps = [rep_graph.pc_false_hyp ((('Err', no_loop_restrs), tag)) for tag in p.pairing.tags] knowledge = EqSearchKnowledge (rep, hyps + err_pc_hyps, list (pc_map)) last_knowledge[0] = knowledge pc_ids = knowledge.classify_vs () id_n_map = logic.dict_list ([(i, n) for (pc, i) in pc_ids.iteritems () for n in pc_map[pc]]) tag_div_ns = [[[n for n in ns if p.node_tags[n][0] == t] for t in tags] for (i, ns) in id_n_map.iteritems ()] split_pairs = [(l_ns[0], r_ns[0]) for (l_ns, r_ns) in tag_div_ns if l_ns and r_ns] p.cached_analysis['init_case_splits'] = split_pairs return split_pairs
def find_split_loop (p, head, restrs, hyps, unfold_limit = 9): assert p.loop_data[head][0] == 'Head' assert p.node_tags[head][0] == p.pairing.tags[0] # the idea is to loop through testable hyps, starting with ones that # need smaller models (the most unfolded models will time out for # large problems like finaliseSlot) rep = mk_graph_slice (p, fast = True) nec = get_necessary_split_opts (p, head, restrs, hyps) if nec and nec[0] == 'CaseSplit': return nec elif nec: i_j_opts = nec else: i_j_opts = default_i_j_opts (unfold_limit) ind_fails = [] for (i_opts, j_opts) in i_j_opts: result = find_split (rep, head, restrs, hyps, i_opts, j_opts) if result[0] != None: return result ind_fails.extend (result[1]) if ind_fails: trace ('Warning: inductive failures: %s' % ind_fails) raise NoSplit ()
def init_loops_to_split (p, restrs): to_split = loops_to_split (p, restrs) rep = mk_graph_slice (p) return [n for n in to_split if not [n2 for n2 in to_split if n2 != n and rep.get_reachable (n2, n)]]
def check_checks(): p = problem.last_problem[0] rep = rep_graph.mk_graph_slice(p) proof = search.last_proof[0] checks = check.proof_checks(p, proof) all_hyps = set([hyp for (_, hyp, _) in checks] + [hyp for (hyps, _, _) in checks for hyp in hyps]) results = [try_interpret_hyp(rep, hyp) for hyp in all_hyps] return [r[1] for r in results if r]
def get_proof_split_limit (p, sp, restrs, hyps, kind, must_find = False): limit = find_split_limit (p, sp, restrs, hyps, kind, must_find = must_find) if limit == None: return None # double-check this limit with a rep constructed without the 'fast' flag limit = find_split_limit (p, sp, restrs, hyps, kind, hints = [limit, limit + 1], use_rep = mk_graph_slice (p)) return (0, limit + 1)
def refute_function_arcs(call_stack, arcs, ctxt_arcs): last_refute_attempt[0] = (call_stack, arcs, ctxt_arcs) f = identify_function(call_stack, [(addr, 0) for arc in arcs for addr in arc]) # easy function limit refutations if not (ctxt_within_function_limits(call_stack) and function_reachable_within_limits(f)): verdicts.setdefault(f, []) if call_stack: vdct = (call_stack, [], "impossible") else: min_addr = min([addr for arc in arcs for addr in arc]) vdct = ([], [min_addr], "impossible") verdicts[f].append(vdct) new_refutes[f] = True print "added %s refutation %s: %s" % (f, vdct[0], vdct[1]) return # ignore complex loops if has_complex_loop(f): print "has loop: %s, skipping" % f return stack = pick_stack_subset(call_stack) arc_extras = call_stack_parent_arc_extras(call_stack, ctxt_arcs, len(stack)) arcs = [arc for arc in arcs if previous_verdict(stack, f, add_arc_extras(arc, arc_extras)) == None] if not arcs: return funs = [body_addrs[addr] for addr in stack] + [f] (p, hyps, addr_map, tag_pairs) = build_compound_problem(funs) rep = rep_graph.mk_graph_slice(p) call_tags = zip(tag_pairs[:-1], tag_pairs[1:]) call_hyps = [get_call_link_hyps(p, addr_map[n], from_tp, to_tp) for (n, (from_tp, to_tp)) in zip(stack, call_tags)] for arc in arcs: arc2 = add_arc_extras(arc, arc_extras) if previous_verdict(stack, f, arc2) != None: continue print "fresh %s arc %s: %s" % (f, stack, arc) vis_pcs = [(get_pc_hyp_local(rep, addr_map[addr]), (addr, i)) for (addr, i) in arc2] vis_pcs = dict(vis_pcs).items() res = refute_minimise_vis_hyps(rep, hyps, call_hyps, vis_pcs) if res == None: verdicts.setdefault(f, []) verdicts[f].append((stack, list(arc2), "possible")) continue (used_call_hyps, used_vis_pcs) = res stack2 = stack[-len(used_call_hyps) :] used_vis = [(addr, i) for (_, (addr, i)) in used_vis_pcs] verdicts.setdefault(f, []) verdicts[f].append((stack2, used_vis, "impossible")) new_refutes[f] = True print "added %s refutation %s: %s" % (f, stack, used_vis)
def get_proof_visit_restr (p, sp, restrs, hyps, kind, must_find = False): rep = rep_graph.mk_graph_slice (p) pc = rep.get_pc ((sp, restrs)) if rep.test_hyp_whyps (pc, hyps): return (1, 2) elif rep.test_hyp_whyps (mk_not (pc), hyps): return (0, 1) else: assert not must_find return None
def failed_test_sets (p, checks): failed = [] sets = {} for (hyps, hyp, name) in checks: sets.setdefault (name, []) sets[name].append ((hyps, hyp)) for name in sets: rep = rep_graph.mk_graph_slice (p) (res, _) = rep.test_hyp_imps (sets[name]) if not res: failed.append (name) return failed
def failed_test_sets(p, checks): failed = [] sets = {} for (hyps, hyp, name) in checks: sets.setdefault(name, []) sets[name].append((hyps, hyp)) for name in sets: rep = rep_graph.mk_graph_slice(p) (res, _, _) = rep.test_hyp_imps(sets[name]) if not res: failed.append(name) return failed
def get_ptr_offsets(p, n_ptrs, bases, hyps=[], cache=None, fail_early=False): """detect which ptrs are guaranteed to be at constant offsets from some set of basis ptrs""" rep = rep_graph.mk_graph_slice(p, fast=True) if cache == None: cache = {} last_get_ptr_offsets[0] = (p, n_ptrs, bases, hyps) smt_bases = [] for (n, ptr, k) in bases: n_vc = default_n_vc(p, n) (_, env) = rep.get_node_pc_env(n_vc) smt = solver.smt_expr(ptr, env, rep.solv) smt_bases.append((smt, k)) ptr_typ = ptr.typ smt_ptrs = [] for (n, ptr) in n_ptrs: n_vc = default_n_vc(p, n) pc_env = rep.get_node_pc_env(n_vc) if not pc_env: continue smt = solver.smt_expr(ptr, pc_env[1], rep.solv) hyp = rep_graph.pc_true_hyp((n_vc, p.node_tags[n][0])) smt_ptrs.append(((n, ptr), smt, hyp)) hyps = hyps + mk_not_callable_hyps(p) for tag in set([p.node_tags[n][0] for (n, _) in n_ptrs]): hyps = hyps + init_correctness_hyps(p, tag) tags = set([p.node_tags[n][0] for (n, ptr) in n_ptrs]) ex_defs = {} for t in tags: ex_defs.update(get_extra_sp_defs(rep, t)) offs = [] for (v, ptr, hyp) in smt_ptrs: off = None for (ptr2, k) in smt_bases: off = offs_expr_const(ptr, ptr2, rep, [hyp] + hyps, cache=cache, extra_defs=ex_defs, typ=ptr_typ) if off != None: offs.append((v, off, k)) break if off == None: trace('get_ptr_offs fallthrough at %d: %s' % v) trace(str([hyp] + hyps)) assert not fail_early, (v, ptr) return offs
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 trace_split_loop_pairs_window (problem, window_size): (_, p, pn, restrs, hyps) = problem loop_head = pn.split[0][0] if ('v_eqs', loop_head) in p.cached_analysis: del p.cached_analysis[('v_eqs', loop_head)] rep = rep_graph.mk_graph_slice (p, fast = True) i_j_opts = search.mk_i_j_opts (unfold_limit = window_size) (i_opts, j_opts) = i_j_opts[-1] knowledge = search.setup_split_search (rep, loop_head, restrs, hyps, i_opts, j_opts, unfold_limit = window_size) res = search.split_search (loop_head, knowledge) trace = list (knowledge.live_pairs_trace) return (res, trace)
def get_prior_loop_heads(p, split, use_rep=None): if use_rep: rep = use_rep else: rep = rep_graph.mk_graph_slice(p) prior = [] split = p.loop_id(split) for h in p.loop_heads(): s = set(prior) if h not in s and rep.get_reachable(h, split) and h != split: # need to recurse to ensure prior are in order prior2 = get_prior_loop_heads(p, h, use_rep=rep) prior.extend([h2 for h2 in prior2 if h2 not in s]) prior.append(h) return prior
def call_stack_parent_arc_extras (stack, ctxt_arcs, max_length): rng = range (1, len (stack))[ -3 : ] arc_extras = [] for i in rng: prev_stack = stack[:i] f = body_addrs[stack[i]] p = functions[f].as_problem (problem.Problem) rep = rep_graph.mk_graph_slice (p) arcs = ctxt_arcs[tuple (prev_stack)] arcs = [a for a in arcs if all_reachable (rep, a + [stack[i]])] arcs = sorted ([(len (a), a) for a in arcs]) if arcs: (_, arc) = arcs[-1] arc_extras.extend ([(addr, len (stack) - i) for addr in arc]) return arc_extras
def find_split_loop (p, head, restrs, hyps): assert p.loop_data[head][0] == 'Head' assert p.node_tags[head][0] == p.pairing.tags[0] # the idea is to loop through testable hyps, starting with ones that # need smaller models (the most unfolded models will time out for # large problems like finaliseSlot) rep = mk_graph_slice (p, fast = True) i_seq_opts = [(0, 1), (1, 1), (2, 1), (3, 1)] j_seq_opts = [(0, 1), (0, 2), (1, 1), (1, 2), (2, 1), (2, 2), (3, 1)] opts_by_lim = {} for (start, step) in i_seq_opts: limit = start + (2 * step) + 1 opts_by_lim.setdefault (limit, ([], [])) opts_by_lim[limit][0].append ((start, step)) for (start, step) in j_seq_opts: limit = start + (2 * step) + 1 opts_by_lim.setdefault (limit, ([], [])) opts_by_lim[limit][1].append ((start, step)) ind_fails = [] i_opts = [] j_opts = [] for unfold_limit in sorted (opts_by_lim): trace ('Split search at %d with unfold limit %d.' % (head, unfold_limit), push = 1) i_opts.extend (opts_by_lim[unfold_limit][0]) j_opts.extend (opts_by_lim[unfold_limit][1]) result = find_split (rep, head, restrs, hyps, i_opts, j_opts, unfold_limit) trace ('Split search with unfold limit %d result: %r' % (unfold_limit, result), push = -1) if result[0] != None: return result ind_fails.extend (result[1]) result = find_case_split (p, head, restrs, hyps) if result[0] != None: return result trace ('All split strategies exhausted.') if ind_fails: trace ('Warning: inductive failures: %s' % ind_fails) raise NoSplit ()
def proof_failed_groups(p=None, proof=None): if p == None: p = problem.last_problem[0] if proof == None: proof = search.last_proof[0] checks = check.proof_checks(p, proof) groups = check.proof_check_groups(checks) failed = [] for group in groups: rep = rep_graph.mk_graph_slice(p) (res, el) = check.test_hyp_group(rep, group) if not res: failed.append(group) print 'Failed element: %s' % el failed_nms = set([s for group in failed for (_, _, s) in group]) print 'Failed: %s' % failed_nms return failed
def getBinaryBoundFromC(p, c_tag, asm_split, restrs, hyps): c_heads = [h for h in search.init_loops_to_split(p, restrs) if p.node_tags[h][0] == c_tag] c_bounds = [(p.loop_id(split), search_bound(p, (), hyps, split)) for split in c_heads] if not [b for (n, b) in c_bounds if b]: trace("no C bounds found (%s)." % c_bounds) return None asm_tag = p.node_tags[asm_split][0] rep = rep_graph.mk_graph_slice(p) i_seq_opts = [(0, 1), (1, 1), (2, 1)] j_seq_opts = [(0, 1), (0, 2), (1, 1)] tags = [p.node_tags[asm_split][0], c_tag] try: split = search.find_split(rep, asm_split, restrs, hyps, i_seq_opts, j_seq_opts, 5, tags=[asm_tag, c_tag]) except solver.SolverFailure, e: 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 proof_failed_groups(p=None, proof=None): if p == None: p = problem.last_problem[0] if proof == None: proof = search.last_proof[0] checks = check.proof_checks(p, proof) groups = check.proof_check_groups(checks) failed = [] for group in groups: rep = rep_graph.mk_graph_slice(p) (res, el) = check.test_hyp_group(rep, group) if not res: failed.append(group) print "Failed element: %s" % el failed_nms = set([s for group in failed for (_, _, s) in group]) print "Failed: %s" % failed_nms return failed
def get_linear_series_eqs(p, split, restrs, hyps, omit_standard=False): k = ('linear_series_eqs', split, restrs, tuple(hyps)) if k in p.cached_analysis: if omit_standard: standard = set(search.mk_seq_eqs(p, split, 1, with_rodata=False)) return set(p.cached_analysis[k]) - standard return p.cached_analysis[k] cands = search.mk_seq_eqs(p, split, 1, with_rodata=False) cands += candidate_additional_eqs(p, split) (tag, _) = p.node_tags[split] rep = rep_graph.mk_graph_slice(p, fast=True) def do_checks(eqs_assume, eqs): checks = (check.single_loop_induct_step_checks( p, restrs, hyps, tag, split, 1, eqs, eqs_assume=eqs_assume) + check.single_loop_induct_base_checks(p, restrs, hyps, tag, split, 1, eqs)) groups = check.proof_check_groups(checks) for group in groups: (res, _) = check.test_hyp_group(rep, group) if not res: return False return True eqs = [] failed = [] while cands: cand = cands.pop() if do_checks(eqs, [cand]): eqs.append(cand) failed.reverse() cands = failed + cands failed = [] else: failed.append(cand) assert do_checks([], eqs) p.cached_analysis[k] = eqs if omit_standard: standard = set(search.mk_seq_eqs(p, split, 1, with_rodata=False)) return set(eqs) - standard return eqs
def default_searcher (p, restrs, hyps): # use any handy init splits res = init_proof_case_split (p, restrs, hyps) if res: return res # detect any un-split loops to_split_init = init_loops_to_split (p, restrs) rep = mk_graph_slice (p, fast = True) l_tag, r_tag = p.pairing.tags l_to_split = [n for n in to_split_init if p.node_tags[n][0] == l_tag] r_to_split = [n for n in to_split_init if p.node_tags[n][0] == r_tag] l_ep = p.get_entry (l_tag) r_ep = p.get_entry (r_tag) for r_sp in r_to_split: trace ('checking loop_no_match at %d' % r_sp, push = 1) if loop_no_match (rep, restrs, hyps, r_sp, l_tag): trace ('loop does not match!', push = -1) return ('Restr', ('Number', [r_sp])) trace (' .. done checking loop no match', push = -1) if l_to_split and not r_to_split: n = l_to_split[0] trace ('lhs loop alone, limit must be found.') return ('Restr', ('Number', [n])) if l_to_split: n = l_to_split[0] trace ('checking lhs loop_no_match at %d' % n, push = 1) if loop_no_match (rep, restrs, hyps, n, r_tag): trace ('loop does not match!', push = -1) return ('Restr', ('Number', [n])) trace (' .. done checking loop no match', push = -1) (kind, split) = find_split_loop (p, n, restrs, hyps) return (kind, split) if r_to_split: n = r_to_split[0] trace ('rhs loop alone, limit must be found.') return ('Restr', ('Number', [n])) return ('Leaf', None)
def findLoopBoundBS(p_n, p, restrs=None, hyps=None, try_seq=None): if hyps == None: hyps = [] # print 'restrs: %s' % str(restrs) if try_seq == None: # bound_try_seq = [1,2,3,4,5,10,50,130,200,260] # bound_try_seq = [0,1,2,3,4,5,10,50,260] calls = [n for n in p.loop_body(p_n) if p.nodes[n].kind == "Call"] if calls: bound_try_seq = [0, 1, 20] else: bound_try_seq = [0, 1, 20, 34] else: bound_try_seq = try_seq rep = mk_graph_slice(p, fast=True) # get the head # print 'Binary addr: %s' % toHexs(self.toPhyAddrs(p_loop_heads)) loop_bound = None p_loop_heads = [n for n in p.loop_data if p.loop_data[n][0] == "Head"] print "p_loop_heads: %s" % p_loop_heads if restrs == None: others = [x for x in p_loop_heads if not x == p_n] # vc_options([concrete numbers], [offsets]) restrs = tuple([(n2, rep_graph.vc_options([0], [1])) for n2 in others]) print "getting the initial bound" # try: index = tryLoopBound(p_n, p, bound_try_seq, rep, restrs=restrs, hyps=hyps) if index == -1: return None print "got the initial bound %d" % bound_try_seq[index] # do a downward binary search to find the concrete loop bound if index == 0: loop_bound = bound_try_seq[0] print "bound = %d" % loop_bound return loop_bound loop_bound = downBinSearch( bound_try_seq[index - 1], bound_try_seq[index], lambda x: tryLoopBound(p_n, p, [x], rep, restrs=restrs, hyps=hyps, bin_return=True), ) print "bound = %d" % loop_bound return loop_bound
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 findLoopBoundBS(p_n, p, restrs=None, hyps=None, try_seq=None): if hyps == None: hyps = [] #print 'restrs: %s' % str(restrs) if try_seq == None: #bound_try_seq = [1,2,3,4,5,10,50,130,200,260] #bound_try_seq = [0,1,2,3,4,5,10,50,260] calls = [n for n in p.loop_body(p_n) if p.nodes[n].kind == 'Call'] if calls: bound_try_seq = [0, 1, 20] else: bound_try_seq = [0, 1, 20, 34] else: bound_try_seq = try_seq rep = mk_graph_slice(p, fast=True) #get the head #print 'Binary addr: %s' % toHexs(self.toPhyAddrs(p_loop_heads)) loop_bound = None p_loop_heads = [n for n in p.loop_data if p.loop_data[n][0] == 'Head'] print 'p_loop_heads: %s' % p_loop_heads if restrs == None: others = [x for x in p_loop_heads if not x == p_n] #vc_options([concrete numbers], [offsets]) restrs = tuple([(n2, rep_graph.vc_options([0], [1])) for n2 in others]) print 'getting the initial bound' #try: index = tryLoopBound(p_n, p, bound_try_seq, rep, restrs=restrs, hyps=hyps) if index == -1: return None print 'got the initial bound %d' % bound_try_seq[index] #do a downward binary search to find the concrete loop bound if index == 0: loop_bound = bound_try_seq[0] print 'bound = %d' % loop_bound return loop_bound loop_bound = downBinSearch( bound_try_seq[index - 1], bound_try_seq[index], lambda x: tryLoopBound( p_n, p, [x], rep, restrs=restrs, hyps=hyps, bin_return=True)) print 'bound = %d' % loop_bound return loop_bound
def get_linear_series_hyps(p, split, restrs, hyps): k = ("linear_series_hyps", split, restrs, tuple(hyps)) if k in p.cached_analysis: return p.cached_analysis[k] cands = search.mk_seq_eqs(p, split, 1, with_rodata=False) cands += candidate_additional_eqs(p, split) (tag, _) = p.node_tags[split] rep = rep_graph.mk_graph_slice(p, fast=True) def do_checks(eqs_assume, eqs): checks = linear_eq_induct_step_checks( p, restrs, hyps, tag, split, eqs_assume, eqs ) + linear_eq_induct_base_checks(p, restrs, hyps, tag, split, eqs) groups = check.proof_check_groups(checks) for group in groups: (res, _) = check.test_hyp_group(rep, group) if not res: return False return True eqs = [] failed = [] while cands: cand = cands.pop() if do_checks(eqs, [cand]): eqs.append(cand) failed.reverse() cands = failed + cands failed = [] else: failed.append(cand) assert do_checks([], eqs) hyps = [h for (h, _) in linear_eq_hyps_at_visit(tag, split, eqs, restrs, vc_offs(0))] p.cached_analysis[k] = hyps return hyps
def check_proof (p, proof, use_rep = None): checks = proof_checks (p, proof) groups = proof_check_groups (checks) for group in groups: if use_rep == None: rep = rep_graph.mk_graph_slice (p) else: rep = use_rep (verdict, elt) = test_hyp_group (rep, group) if verdict: continue (hyps, hyp, name) = elt last_failed_check[0] = elt trace ('%s: proof failed!' % name) return False if save_checked_proofs[0]: save = save_checked_proofs[0] save (p, proof) return True
def get_ptr_offsets (p, n_ptrs, bases, hyps = []): """detect which ptrs are guaranteed to be at constant offsets from some set of basis ptrs""" rep = rep_graph.mk_graph_slice (p, fast = True) cache = {} last_get_ptr_offsets[0] = (p, n_ptrs, bases, hyps) smt_bases = [] for (n, ptr, k) in bases: n_vc = default_n_vc (p, n) (_, env) = rep.get_node_pc_env (n_vc) smt = solver.smt_expr (ptr, env, rep.solv) smt_bases.append ((smt, k)) smt_ptrs = [] for (n, ptr) in n_ptrs: n_vc = default_n_vc (p, n) pc_env = rep.get_node_pc_env (n_vc) if not pc_env: continue smt = solver.smt_expr (ptr, pc_env[1], rep.solv) hyp = rep_graph.pc_true_hyp ((n_vc, p.node_tags[n][0])) smt_ptrs.append (((n, ptr), smt, hyp)) hyps = hyps + mk_not_callable_hyps (p) tags = set ([p.node_tags[n][0] for (n, ptr) in n_ptrs]) ex_defs = {} for t in tags: ex_defs.update (get_extra_sp_defs (rep, t)) offs = [] for (v, ptr, hyp) in smt_ptrs: for (ptr2, k) in smt_bases: off = offs_expr_const (ptr, ptr2, rep, [hyp] + hyps, cache = cache, extra_defs = ex_defs) if off != None: offs.append ((v, off, k)) break trace ('get_ptr_offs fallthrough at %d: %s' % v) return offs
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 search_bound(p, restrs, hyps, split): last_search_bound[0] = (p, restrs, hyps, split) # try a naive bin search first # limit this to a small bound for time purposes # - for larger bounds the less naive approach can be faster bound = findLoopBoundBS(split, p, restrs=restrs, hyps=hyps, try_seq=[0, 1, 6]) if bound != None: return (bound, 'NaiveBinSearch') l_hyps = get_linear_series_hyps(p, split, restrs, hyps) rep = rep_graph.mk_graph_slice(p, fast=True) def test(n): assert n > 10 hyp = check.mk_loop_counter_eq_hyp(p, split, restrs, n - 2) visit = ((split, vc_offs(2)), ) + restrs continue_to_split_guess = rep.get_pc((split, visit)) return rep.test_hyp_whyps(syntax.mk_not(continue_to_split_guess), [hyp] + l_hyps + hyps) # findLoopBoundBS always checks to at least 16 min_bound = 16 max_bound = max_acceptable_bound[0] bound = upDownBinSearch(min_bound, max_bound, test) if bound != None and test(bound): return (bound, 'InductiveBinSearch') # let the naive bin search go a bit further bound = findLoopBoundBS(split, p, restrs=restrs, hyps=hyps) if bound != None: return (bound, 'NaiveBinSearch') return None
def check_proof(p, proof, use_rep=None): checks = proof_checks(p, proof) groups = proof_check_groups(checks) for group in groups: if use_rep == None: rep = rep_graph.mk_graph_slice(p) else: rep = use_rep detail = [0] (verdict, elt) = test_hyp_group(rep, group, detail) if verdict: continue (hyps, hyp, name) = elt last_failed_check[0] = elt trace('%s: proof failed!' % name) trace(' (failure kind: %r)' % detail[0]) return False if save_checked_proofs[0]: save = save_checked_proofs[0] save(p, proof) return True
def build_proof_rec_with_restrs (split_points, kind, searcher, p, restrs, hyps): sp = split_points[0] use_hyps = list (hyps) if p.node_tags[sp][0] != p.pairing.tags[1]: nrerr_hyp = check.non_r_err_pc_hyp (p.pairing.tags, restr_others (p, restrs, 2)) use_hyps = use_hyps + [nrerr_hyp] limit = find_split_limit (p, sp, restrs, use_hyps, kind) # double-check this limit with a rep constructed without the 'fast' flag limit = find_split_limit (p, sp, restrs, use_hyps, kind, hints = [limit, limit + 1], use_rep = mk_graph_slice (p)) if kind == 'Number': vc_opts = vc_upto (limit + 1) else: vc_opts = vc_offset_upto (limit + 1) restrs = restrs + ((sp, vc_opts), ) if len (split_points) == 1: subproof = build_proof_rec (searcher, p, restrs, hyps) else: subproof = build_proof_rec_with_restrs (split_points[1:], kind, searcher, p, restrs, hyps) return ProofNode ('Restr', (sp, (kind, (0, limit + 1))), [subproof])
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 init_proof_case_split (p, restrs, hyps): ps = init_case_splits (p, hyps) if ps == None: return None p.cached_analysis.setdefault ('finished_init_case_splits', []) fin = p.cached_analysis['finished_init_case_splits'] known_s = set.union (set (restrs), set (hyps)) for rs in fin: if rs <= known_s: return None rep = rep_graph.mk_graph_slice (p) no_loop_restrs = tuple ([(n, vc_num (0)) for n in p.loop_heads ()]) err_pc_hyps = [rep_graph.pc_false_hyp ((('Err', no_loop_restrs), tag)) for tag in p.pairing.tags] for (n1, n2) in ps: pc = rep.get_pc ((n1, ())) if rep.test_hyp_whyps (pc, hyps + err_pc_hyps): continue if rep.test_hyp_whyps (mk_not (pc), hyps + err_pc_hyps): continue case_split_tr.append ((n1, restrs, hyps)) return ('CaseSplit', ((n1, p.node_tags[n1][0]), [n1, n2])) fin.append (known_s) return None
last_asm_stack_depth_fun = [0] def check_before_guess_asm_stack_depth(fun): from solver import smt_expr if not fun.entry: return None p = fun.as_problem(problem.Problem, name='Target') try: p.do_analysis() p.check_no_inner_loops() inline_no_pre_pairing(p) except problem.Abort, e: return None rep = rep_graph.mk_graph_slice(p, fast=True) try: rep.get_pc(default_n_vc(p, 'Ret'), 'Target') err_pc = rep.get_pc(default_n_vc(p, 'Err'), 'Target') except solver.EnvMiss, e: return None inlined_funs = set([fn for (_, _, fn) in p.inline_scripts['Target']]) if inlined_funs: printout(' (stack analysis also involves %s)' % ', '.join(inlined_funs)) return p def guess_asm_stack_depth(fun):
asm_tag = p.node_tags[asm_split][0] rep = rep_graph.mk_graph_slice(p) i_seq_opts = [(0, 1), (1, 1), (2, 1)] j_seq_opts = [(0, 1), (0, 2), (1, 1)] tags = [p.node_tags[asm_split][0], c_tag] try: split = search.find_split(rep, asm_split, restrs, hyps, i_seq_opts, j_seq_opts, 5, tags=[asm_tag, c_tag]) except solver.SolverFailure, e: return None if not split or split[0] != "Split": trace("no split found (%s)." % repr(split)) return None (_, split) = split rep = rep_graph.mk_graph_slice(p) checks = check.split_checks(p, (), hyps, split, tags=[asm_tag, c_tag]) groups = check.proof_check_groups(checks) try: for group in groups: (res, el) = check.test_hyp_group(rep, group) if not res: trace("split check failed!") trace("failed at %s" % el) return None except solver.SolverFailure, e: return None (as_details, c_details, _, n, _) = split (c_split, (seq_start, step), _) = c_details c_bound = dict(c_bounds).get(p.loop_id(c_split)) if not c_bound:
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"
try: split = search.find_split(rep, asm_split, restrs, hyps, i_seq_opts, j_seq_opts, 5, tags=[asm_tag, c_tag]) except solver.SolverFailure, e: return None if not split or split[0] != 'Split': trace('no split found (%s).' % repr(split)) return None (_, split) = split rep = rep_graph.mk_graph_slice(p) checks = check.split_checks(p, (), hyps, split, tags=[asm_tag, c_tag]) groups = check.proof_check_groups(checks) try: for group in groups: (res, el) = check.test_hyp_group(rep, group) if not res: trace('split check failed!') trace('failed at %s' % el) return None except solver.SolverFailure, e: return None (as_details, c_details, _, n, _) = split (c_split, (seq_start, step), _) = c_details c_bound = dict(c_bounds).get(p.loop_id(c_split)) if not c_bound:
def check_proof_report_rec(p, restrs, hyps, proof, step_num, ctxt, inducts, do_check=True): printout('Step %d: %s' % (step_num, ctxt)) if proof.kind == 'Restr': (kind, (x, y)) = proof.restr_range if kind == 'Offset': v = inducts[1][proof.point] rexpr = '{%s + %s ..< %s + %s}' % (v, x, v, y) else: rexpr = '{%s ..< %s}' % (x, y) printout(' Prove the number of visits to %d is in %s' % (proof.point, rexpr)) checks = proof_restr_checks(proof.point, proof.restr_range, p, restrs, hyps) cases = [''] elif proof.kind == 'SingleRevInduct': printout(' Proving a predicate by future induction.') (eqs, n) = proof.eqs_proof point = proof.point printout(' proving these invariants by %d-induction' % n) for x in eqs: printout(' %s (@ addr %s)' % (pretty_lambda(x), point)) printout(' then establishing this predicate') (pred, n_bound) = proof.rev_proof printout(' %s (@ addr %s)' % (pretty_lambda(pred), point)) printout(' at large iterations (%d) and by back induction.' % n_bound) cases = [''] checks = all_rev_induct_checks(p, restrs, hyps, point, proof.eqs_proof, proof.rev_proof) elif proof.kind == 'Split': (l_dts, r_dts, eqs, n, lrmx) = proof.split v = next_induct_var(inducts[0]) inducts = (inducts[0] + 1, dict(inducts[1])) inducts[1][l_dts[0]] = v inducts[1][r_dts[0]] = v printout(' prove %s related to %s' % (pretty_vseq(l_dts), pretty_vseq(r_dts))) printout(' with equalities') for (x, y) in eqs: printout(' %s (@ addr %s)' % (pretty_lambda(x), l_dts[0])) printout(' = %s (@ addr %s)' % (pretty_lambda(y), r_dts[0])) printout(' and with invariants') for x in l_dts[2]: printout(' %s (@ addr %s)' % (pretty_lambda(x), l_dts[0])) for x in r_dts[2]: printout(' %s (@ addr %s)' % (pretty_lambda(x), r_dts[0])) checks = split_checks(p, restrs, hyps, proof.split) cases = [ 'case in (%d) where the length of the sequence < %d' % (step_num, n), 'case in (%d) where the length of the sequence is %s + %s' % (step_num, v, n) ] elif proof.kind == 'Leaf': printout(' prove all verification conditions') checks = leaf_condition_checks(p, restrs, hyps) cases = [] elif proof.kind == 'CaseSplit': printout(' case split on whether %d is visited' % proof.point) checks = [] cases = [ 'case in (%d) where %d is visited' % (step_num, proof.point), 'case in (%d) where %d is not visited' % (step_num, proof.point) ] if checks and do_check: groups = proof_check_groups(checks) for group in groups: rep = rep_graph.mk_graph_slice(p) detail = [0] (res, _) = test_hyp_group(rep, group, detail) if not res: printout(' .. failed to prove this.') printout(' (failure kind: %r)' % detail[0]) return printout(' .. proven.') subproblems = proof_subproblems(p, proof.kind, proof.args, restrs, hyps, '') xs = logic.azip(subproblems, proof.subproofs) xs = logic.azip(xs, cases) step_num += 1 for ((subprob, subproof), case) in xs: (restrs, hyps, _) = subprob res = check_proof_report_rec(p, restrs, hyps, subproof, step_num, case, inducts, do_check=do_check) if not res: return (step_num, induct_var_num) = res inducts = (induct_var_num, inducts[1]) return (step_num, inducts[0])
p.cached_analysis[key] = va2 return va2 last_asm_stack_depth_fun = [0] def check_before_guess_asm_stack_depth (fun): from solver import smt_expr if not fun.entry: return None p = fun.as_problem (problem.Problem, name = 'Target') try: p.do_analysis () p.check_no_inner_loops () except problem.Abort, e: return None rep = rep_graph.mk_graph_slice (p, fast = True) try: rep.get_pc (default_n_vc (p, 'Ret'), 'Target') err_pc = rep.get_pc (default_n_vc (p, 'Err'), 'Target') except solver.EnvMiss, e: return None return p def guess_asm_stack_depth (fun): p = check_before_guess_asm_stack_depth (fun) if not p: return (0, {}) last_asm_stack_depth_fun[0] = fun.name entry = p.get_entry ('Target')
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 refute_function_arcs(call_stack, arcs, ctxt_arcs): last_refute_attempt[0] = (call_stack, arcs, ctxt_arcs) f = identify_function(call_stack, [(addr, 0) for arc in arcs for addr in arc]) # easy function limit refutations if not (ctxt_within_function_limits(call_stack) and function_reachable_within_limits(f)): verdicts.setdefault(f, []) if call_stack: vdct = (call_stack, [], 'impossible') else: min_addr = min([addr for arc in arcs for addr in arc]) vdct = ([], [min_addr], 'impossible') verdicts[f].append(vdct) new_refutes[f] = True print 'added %s refutation %s: %s' % (f, vdct[0], vdct[1]) return # ignore complex loops if has_complex_loop(f): print 'has complex loop: %s, skipping' % f return (top_loop, stack) = pick_stack_setup(call_stack) arc_extras = call_stack_parent_arc_extras(call_stack, ctxt_arcs, len(stack), top_loop) arcs = [ arc for arc in arcs if previous_verdict(stack, f, add_arc_extras(arc, arc_extras)) == None ] if not arcs: return funs = [body_addrs[addr] for addr in stack] + [f] (p, hyps, addr_map, tag_pairs) = build_compound_problem(funs) focused_loops = {} if top_loop != None: top_loop_split = p.loop_id(addr_map[top_loop]) top_loop_tag = p.node_tags[top_loop_split][0] assert top_loop_tag in tag_pairs[0][0].values() focused_loops[top_loop_tag] = top_loop_split rep = rep_graph.mk_graph_slice(p) call_tags = zip(tag_pairs[:-1], tag_pairs[1:]) call_hyps = [ get_call_link_hyps(p, addr_map[n], from_tp, to_tp, focused_loops=focused_loops) for (n, (from_tp, to_tp)) in zip(stack, call_tags) ] for arc in arcs: arc2 = add_arc_extras(arc, arc_extras) if previous_verdict(stack, f, arc2) != None: continue print 'fresh %s arc %s: %s' % (f, stack, arc) vis_pcs = [(get_pc_hyp_local(rep, addr_map[addr], focused_loops=focused_loops), (addr, i)) for (addr, i) in arc2] vis_pcs = dict(vis_pcs).items() res = refute_minimise_vis_hyps(rep, hyps, call_hyps, vis_pcs) if res == None: verdicts.setdefault(f, []) verdicts[f].append((stack, list(arc2), 'possible')) continue (used_call_hyps, used_vis_pcs) = res stack2 = stack[-len(used_call_hyps):] used_vis = [(addr, i) for (_, (addr, i)) in used_vis_pcs] verdicts.setdefault(f, []) if len(stack2) == len(stack) and top_loop != None: vdct = 'impossible_in_loop' else: vdct = 'impossible' verdicts[f].append((stack2, used_vis, vdct)) new_refutes[f] = True print 'added %s refutation %s: %s' % (f, stack, used_vis)