def split_hyps_at_visit (tags, split, restrs, visit): (l_details, r_details, eqs, _, _) = split (l_split, (l_seq_start, l_step), l_eqs) = l_details (r_split, (r_seq_start, r_step), r_eqs) = r_details (l_visit, r_visit) = split_visit_visits (tags, split, restrs, visit) (l_start, r_start) = split_visit_visits (tags, split, restrs, vc_num (0)) (l_tag, r_tag) = tags def mksub (v): return lambda exp: logic.var_subst (exp, {('%i', word32T) : v}, must_subst = False) def inst (exp): return logic.inst_eq_at_visit (exp, visit) zsub = mksub (mk_word32 (0)) if visit.kind == 'Number': lsub = mksub (mk_word32 (visit.n)) else: lsub = mksub (mk_plus (mk_var ('%n', word32T), mk_word32 (visit.n))) hyps = [(Hyp ('PCImp', l_visit, r_visit), 'pc imp'), (Hyp ('PCImp', l_visit, l_start), '%s pc imp' % l_tag), (Hyp ('PCImp', r_visit, r_start), '%s pc imp' % r_tag)] hyps += [(eq_hyp ((zsub (l_exp), l_start), (lsub (l_exp), l_visit), (l_split, r_split)), '%s const' % l_tag) for l_exp in l_eqs if inst (l_exp)] hyps += [(eq_hyp ((zsub (r_exp), r_start), (lsub (r_exp), r_visit), (l_split, r_split)), '%s const' % r_tag) for r_exp in r_eqs if inst (r_exp)] hyps += [(eq_hyp ((lsub (l_exp), l_visit), (lsub (r_exp), r_visit), (l_split, r_split)), 'eq') for (l_exp, r_exp) in eqs if inst (l_exp) and inst (r_exp)] return hyps
def apply_rel_wrapper (lhs, rhs): assert lhs.typ == syntax.builtinTs['RelWrapper'] assert rhs.typ == syntax.builtinTs['RelWrapper'] assert lhs.kind == 'Op' assert rhs.kind == 'Op' ops = set ([lhs.name, rhs.name]) if ops == set (['StackWrapper']): [sp1, st1] = lhs.vals[:2] [sp2, st2] = rhs.vals[:2] excepts = list (set (lhs.vals[2:] + rhs.vals[2:])) for p in excepts: st1 = syntax.mk_memupd (st1, p, syntax.mk_word32 (0)) st2 = syntax.mk_memupd (st2, p, syntax.mk_word32 (0)) return syntax.Expr ('Op', boolT, name = 'StackEquals', vals = [sp1, st1, sp2, st2]) elif ops == set (['MemAccWrapper', 'MemWrapper']): [acc] = [v for v in [lhs, rhs] if v.is_op ('MemAccWrapper')] [addr, val] = acc.vals assert addr.typ == syntax.word32T [m] = [v for v in [lhs, rhs] if v.is_op ('MemWrapper')] [m] = m.vals assert m.typ == builtinTs['Mem'] expr = mk_eq (mk_memacc (m, addr, val.typ), val) return expr elif ops == set (['EqSelectiveWrapper']): [lhs_v, _, _] = lhs.vals [rhs_v, _, _] = rhs.vals if lhs_v.typ == syntax.builtinTs['RelWrapper']: return apply_rel_wrapper (lhs_v, rhs_v) else: return mk_eq (lhs, rhs) else: assert not 'rel wrapper opname understood'
def split_hyps_at_visit(tags, split, restrs, visit): (l_details, r_details, eqs, _, _) = split (l_split, (l_seq_start, l_step), l_eqs) = l_details (r_split, (r_seq_start, r_step), r_eqs) = r_details (l_visit, r_visit) = split_visit_visits(tags, split, restrs, visit) (l_start, r_start) = split_visit_visits(tags, split, restrs, vc_num(0)) (l_tag, r_tag) = tags def mksub(v): return lambda exp: logic.var_subst(exp, {('%i', word32T): v}, must_subst=False) def inst(exp): return logic.inst_eq_at_visit(exp, visit) zsub = mksub(mk_word32(0)) if visit.kind == 'Number': lsub = mksub(mk_word32(visit.n)) else: lsub = mksub(mk_plus(mk_var('%n', word32T), mk_word32(visit.n))) hyps = [(Hyp('PCImp', l_visit, r_visit), 'pc imp'), (Hyp('PCImp', l_visit, l_start), '%s pc imp' % l_tag), (Hyp('PCImp', r_visit, r_start), '%s pc imp' % r_tag)] hyps += [(eq_hyp((zsub(l_exp), l_start), (lsub(l_exp), l_visit), (l_split, r_split)), '%s const' % l_tag) for l_exp in l_eqs if inst(l_exp)] hyps += [(eq_hyp((zsub(r_exp), r_start), (lsub(r_exp), r_visit), (l_split, r_split)), '%s const' % r_tag) for r_exp in r_eqs if inst(r_exp)] hyps += [(eq_hyp((lsub(l_exp), l_visit), (lsub(r_exp), r_visit), (l_split, r_split)), 'eq') for (l_exp, r_exp) in eqs if inst(l_exp) and inst(r_exp)] return hyps
def stack_bounds_to_closed_form (bounds, names, idents): closed = {} for fname in names: res = syntax.mk_word32 (bounds[(fname, syntax.true_term)]) extras = [] if fname in idents: assert idents[fname][-1] == syntax.true_term extras = reversed (idents[fname][:-1]) for ident in extras: alt = syntax.mk_word32 (bounds[(fname, ident)]) res = syntax.mk_if (ident, alt, res) closed[fname] = res return closed
def stack_bounds_to_closed_form(bounds, names, idents): closed = {} for fname in names: res = syntax.mk_word32(bounds[(fname, syntax.true_term)]) extras = [] if fname in idents: assert idents[fname][-1] == syntax.true_term extras = reversed(idents[fname][:-1]) for ident in extras: alt = syntax.mk_word32(bounds[(fname, ident)]) res = syntax.mk_if(ident, alt, res) closed[fname] = res return closed
def get_induct_eq_hyp(p, split, restrs, n): details = (split, (0, 1), []) (tag, _) = p.node_tags[split] visit = split_visit_one_visit(tag, details, restrs, vc_offs(0)) from syntax import mk_var, word32T, mk_word32 return eq_hyp((mk_var("%n", word32T), visit), (mk_word32(n), visit), (split, 0))
def build_rodata (rodata_stream): rodata = {} for line in rodata_stream: if not is_rodata_line.match (line): continue bits = line.split () rodata[int (bits[0][:-1], 16)] = int (bits[1], 16) rodata_min = min (rodata.keys ()) rodata_max = max (rodata.keys ()) + 4 assert rodata_min % 4 == 0 rodata_range = range (rodata_min, rodata_max, 4) for x in rodata_range: if x not in rodata: trace ('.rodata section gap at address %x' % x) struct_name = fresh_name ('rodata', structs) struct = Struct (struct_name, rodata_max - rodata_min, 1) structs[struct_name] = struct (start, end) = sections['.rodata'] assert start <= rodata_min assert end + 1 >= rodata_max return (rodata, mk_word32 (rodata_min), struct.typ)
def mk_seq_eqs (p, split, step, with_rodata): # eqs take the form of a number of constant expressions eqs = [] # the variable 'loop' will be converted to the point in # the sequence - note this should be multiplied by the step size loop = mk_var ('%i', word32T) if step == 1: minus_loop_step = mk_uminus (loop) else: minus_loop_step = mk_times (loop, mk_word32 (- step)) for (var, data) in get_loop_var_analysis_at (p, split): if data == 'LoopVariable': if with_rodata and var.typ == builtinTs['Mem']: eqs.append (logic.mk_rodata (var)) elif data == 'LoopConst': if var.typ not in syntax.phantom_types: eqs.append (var) elif data == 'LoopLeaf': continue elif data[0] == 'LoopLinearSeries': (_, form, _) = data eqs.append (form (var, minus_loop_step)) else: assert not 'var_deps type understood' return eqs
def v_eqs_to_split (p, pair, v_eqs, restrs, hyps, tags = None): trace ('v_eqs_to_split: (%s, %s)' % pair) ((l_n, l_init, l_step), (r_n, r_init, r_step)) = pair l_details = (l_n, (l_init, l_step), mk_seq_eqs (p, l_n, l_step, True) + [v_i[0] for (v_i, v_j) in v_eqs if v_j == 'Const']) r_details = (r_n, (r_init, r_step), mk_seq_eqs (p, r_n, r_step, False) + c_memory_loop_invariant (p, r_n, l_n)) eqs = [(v_i[0], mk_cast (v_j[0], v_i[0].typ)) for (v_i, v_j) in v_eqs if v_j != 'Const' if v_i[0] != syntax.mk_word32 (0)] n = 2 split = (l_details, r_details, eqs, n, (n * r_step) - 1) trace ('Split: %s' % (split, )) if tags == None: tags = p.pairing.tags hyps = hyps + check.split_loop_hyps (tags, split, restrs, exit = True) r_max = find_split_limit (p, r_n, restrs, hyps, 'Offset', bound = (n + 2) * r_step, must_find = False, hints = [n * r_step, n * r_step + 1]) if r_max == None: trace ('v_eqs_to_split: no RHS limit') return None if r_max > n * r_step: trace ('v_eqs_to_split: RHS limit not %d' % (n * r_step)) return None trace ('v_eqs_to_split: split %s' % (split,)) return split
def mk_local(n, kind, off, k): (v, off2) = sp_reps[n][k] ptr = syntax.mk_plus(v, syntax.mk_word32(off + off2)) if kind == 'Ptr': return ptr elif kind == 'MemAcc': return syntax.mk_memacc(stack, ptr, syntax.word32T)
def mk_local (n, kind, off, k): (v, off2) = sp_reps[n][k] ptr = syntax.mk_plus (v, syntax.mk_word32 (off + off2)) if kind == 'Ptr': return ptr elif kind == 'MemAcc': return syntax.mk_memacc (stack, ptr, syntax.word32T)
def stack_virtualise_expr(expr, sp_offs): if expr.is_op('MemAcc') and is_stack(expr.vals[0]): [m, p] = expr.vals if expr.typ == syntax.word8T: ps = [(syntax.mk_minus(p, syntax.mk_word32(n)), n) for n in [0, 1, 2, 3]] elif expr.typ == syntax.word32T: ps = [(p, 0)] else: assert expr.typ == syntax.word32T, expr ptrs = [(p, 'MemAcc') for (p, _) in ps] if sp_offs == None: return (ptrs, None) # FIXME: very 32-bit specific ps = [(p, n) for (p, n) in ps if p in sp_offs if sp_offs[p][1] % 4 == 0] if not ps: return (ptrs, expr) [(p, n)] = ps if p not in sp_offs: raise StackOffsMissing() (k, offs) = sp_offs[p] v = mk_var(('Fake', k, offs), syntax.word32T) if n != 0: v = syntax.mk_shiftr(v, n * 8) v = syntax.mk_cast(v, expr.typ) return (ptrs, v) elif expr.kind == 'Op': vs = [stack_virtualise_expr(v, sp_offs) for v in expr.vals] return ([p for (ptrs, _) in vs for p in ptrs], syntax.adjust_op_vals(expr, [v for (_, v) in vs])) else: return ([], expr)
def update_v_ids_for_model (knowledge, pairs, vs, m): rep = knowledge.rep # first update the live variables groups = {} for v in vs: (k, const) = vs[v] groups.setdefault (k, []) groups[k].append ((v, const)) k_counter = 1 vs.clear () for k in groups: for (const, xs) in split_group (knowledge, m, groups[k]): for x in xs: vs[x] = (k_counter, const) k_counter += 1 # then figure out which pairings are still viable needed_ks = set () zero = syntax.mk_word32 (0) for (pair, data) in pairs.items (): if data[0] == 'Failed': continue (lvs, rvs) = data lv_ks = set ([vs[v][0] for v in lvs if v[0] == zero or not vs[v][1]]) rv_ks = set ([vs[v][0] for v in rvs]) miss_vars = lv_ks - rv_ks if miss_vars: lv_miss = [v[0] for v in lvs if vs[v][0] in miss_vars] pairs[pair] = ('Failed', lv_miss.pop ()) else: needed_ks.update ([vs[v][0] for v in lvs + rvs]) # then drop any vars which are no longer relevant for v in vs.keys (): if vs[v][0] not in needed_ks: del vs[v]
def stack_virtualise_expr (expr, sp_offs): if expr.is_op ('MemAcc') and is_stack (expr.vals[0]): [m, p] = expr.vals if expr.typ == syntax.word8T: ps = [(syntax.mk_minus (p, syntax.mk_word32 (n)), n) for n in [0, 1, 2, 3]] elif expr.typ == syntax.word32T: ps = [(p, 0)] else: assert expr.typ == syntax.word32T, expr ptrs = [(p, 'MemAcc') for (p, _) in ps] if sp_offs == None: return (ptrs, None) # FIXME: very 32-bit specific ps = [(p, n) for (p, n) in ps if p in sp_offs if sp_offs[p][1] % 4 == 0] if not ps: return (ptrs, expr) [(p, n)] = ps (k, offs) = sp_offs[p] v = mk_var (('Fake', k, offs), syntax.word32T) if n != 0: v = syntax.mk_shiftr (v, n * 8) v = syntax.mk_cast (v, expr.typ) return (ptrs, v) elif expr.kind == 'Op': vs = [stack_virtualise_expr (v, sp_offs) for v in expr.vals] return ([p for (ptrs, _) in vs for p in ptrs], syntax.Expr ('Op', expr.typ, name = expr.name, vals = [v for (_, v) in vs])) else: return ([], expr)
def build_rodata(rodata_stream, rodata_ranges=[('Section', '.rodata')]): from syntax import structs, fresh_name, Struct, mk_word32 import syntax from target_objects import symbols, sections, trace act_rodata_ranges = [] for (kind, nm) in rodata_ranges: if kind == 'Symbol': (addr, size, _) = symbols[nm] act_rodata_ranges.append((addr, addr + size - 1)) elif kind == 'Section': if nm in sections: act_rodata_ranges.append(sections[nm]) else: # it's reasonable to supply .rodata as the # expected section only for it to be missing trace('No %r section in objdump.' % nm) else: assert kind in ['Symbol', 'Section'], rodata_ranges comb_ranges = [] for (start, end) in sorted(act_rodata_ranges): if comb_ranges and comb_ranges[-1][1] + 1 == start: (start, _) = comb_ranges[-1] comb_ranges[-1] = (start, end) else: comb_ranges.append((start, end)) rodata = {} for line in rodata_stream: if not is_rodata_line.match(line): continue bits = line.split() (addr, v) = (int(bits[0][:-1], 16), int(bits[1], 16)) if [ 1 for (start, end) in comb_ranges if start <= addr and addr <= end ]: assert addr % 4 == 0, addr rodata[addr] = v if len(comb_ranges) == 1: rodata_names = ['rodata_struct'] else: rodata_names = [ 'rodata_struct_%d' % (i + 1) for (i, _) in enumerate(comb_ranges) ] rodata_ptrs = [] for ((start, end), name) in zip(comb_ranges, rodata_names): struct_name = fresh_name(name, structs) struct = Struct(struct_name, (end - start) + 1, 1) structs[struct_name] = struct typ = syntax.get_global_wrapper(struct.typ) rodata_ptrs.append((mk_word32(start), typ)) return (rodata, comb_ranges, rodata_ptrs)
def loop_var_analysis(p, split): """computes the same loop dataflow analysis as in the 'logic' module but with stack slots treated as virtual variables.""" if not is_asm_node(p, split): return None head = p.loop_id(split) tag = p.node_tags[split][0] assert head key = ('loop_stack_virtual_var_cycle_analysis', split) if key in p.cached_analysis: return p.cached_analysis[key] (vds, adj_nodes, loc_offs, upd_offsets, _) = get_loop_virtual_stack_analysis(p, tag) loop = p.loop_body(head) va = logic.compute_loop_var_analysis(p, vds, split, override_nodes=adj_nodes) (stack, _) = get_stack_sp(p, tag) va2 = [] uoffs = upd_offsets.get(head, []) for (v, data) in va: if v.kind == 'Var' and v.name[0] == 'Fake': (_, k, offs) = v.name if (k, offs) not in uoffs: continue v2 = loc_offs(split, 'MemAcc', offs, k) va2.append((v2, data)) elif v.kind == 'Var' and v.name.startswith('stack'): assert v.typ == stack.typ continue else: va2.append((v, data)) stack_const = stack for (k, off) in uoffs: stack_const = syntax.mk_memupd(stack_const, loc_offs(split, 'Ptr', off, k), syntax.mk_word32(0)) sp = asm_stack_rep_hook(p, (stack.name, stack.typ), 'Loop', split) assert sp and sp[0] == 'SplitMem', (split, sp) (_, st_split) = sp stack_const = logic.mk_stack_wrapper(st_split, stack_const, []) stack_const = logic.mk_eq_selective_wrapper(stack_const, ([], [0])) va2.append((stack_const, 'LoopConst')) p.cached_analysis[key] = va2 return va2
def loop_eq_hyps_at_visit(tag, split, eqs, restrs, visit_num, use_if_at=False): details = (split, (0, 1), eqs) visit = split_visit_one_visit(tag, details, restrs, visit_num) start = split_visit_one_visit(tag, details, restrs, vc_num(0)) def mksub(v): return lambda exp: logic.var_subst(exp, {('%i', word32T): v}, must_subst=False) zsub = mksub(mk_word32(0)) if visit_num.kind == 'Number': isub = mksub(mk_word32(visit_num.n)) else: isub = mksub(mk_plus(mk_var('%n', word32T), mk_word32(visit_num.n))) hyps = [(Hyp('PCImp', visit, start), '%s pc imp' % tag)] hyps += [(eq_hyp((zsub(exp), start), (isub(exp), visit), (split, 0), use_if_at=use_if_at), '%s const' % tag) for exp in eqs if logic.inst_eq_at_visit(exp, visit_num)] return hyps
def loop_var_analysis (p, split): """computes the same loop dataflow analysis as in the 'logic' module but with stack slots treated as virtual variables.""" if not is_asm_node (p, split): return None head = p.loop_id (split) tag = p.node_tags[split][0] assert head key = ('loop_stack_virtual_var_cycle_analysis', split) if key in p.cached_analysis: return p.cached_analysis[key] (vds, adj_nodes, loc_offs, upd_offsets, _) = get_loop_virtual_stack_analysis (p, tag) loop = p.loop_body (head) va = logic.compute_loop_var_analysis (adj_nodes, vds, split, loop, p.preds) (stack, _) = get_stack_sp (p, tag) va2 = [] uoffs = upd_offsets.get (head, []) for (v, data) in va: if v.kind == 'Var' and v.name[0] == 'Fake': (_, k, offs) = v.name if (k, offs) not in uoffs: continue v2 = loc_offs (split, 'MemAcc', offs, k) va2.append ((v2, data)) elif v.kind == 'Var' and v.name.startswith ('stack'): assert v.typ == stack.typ continue else: va2.append ((v, data)) stack_const = stack for (k, off) in uoffs: stack_const = syntax.mk_memupd (stack_const, loc_offs (split, 'Ptr', off, k), syntax.mk_word32 (0)) sp = asm_stack_rep_hook (p, (stack.name, stack.typ), 'Loop', split) assert sp and sp[0] == 'SplitMem', (split, sp) (_, st_split) = sp stack_const = logic.mk_stack_wrapper (st_split, stack_const, []) stack_const = logic.mk_eq_selective_wrapper (stack_const, ([], [0])) va2.append ((stack_const, 'LoopConst')) p.cached_analysis[key] = va2 return va2
def linear_eq_hyps_at_visit (tag, split, eqs, restrs, visit_num): details = (split, (0, 1), eqs) visit = split_visit_one_visit (tag, details, restrs, visit_num) start = split_visit_one_visit (tag, details, restrs, vc_num (0)) from syntax import mk_word32, mk_plus, mk_var, word32T def mksub (v): return lambda exp: logic.var_subst (exp, {('%i', word32T) : v}, must_subst = False) zsub = mksub (mk_word32 (0)) if visit_num.kind == 'Number': isub = mksub (mk_word32 (visit_num.n)) else: isub = mksub (mk_plus (mk_var ('%n', word32T), mk_word32 (visit_num.n))) hyps = [(Hyp ('PCImp', visit, start), '%s pc imp' % tag)] hyps += [(eq_hyp ((zsub (exp), start), (isub (exp), visit), (split, 0)), '%s const' % tag) for exp in eqs if logic.inst_eq_at_visit (exp, visit_num)] return hyps
def mk_pairing_v_eqs (knowledge, pair, endorsed = True): v_eqs = [] (lvs, rvs) = knowledge.pairs[pair] zero = mk_word32 (0) for v_i in lvs: (k, const) = knowledge.v_ids[v_i] if const and v_i[0] != zero: if not endorsed or eq_known (knowledge, (v_i, 'Const')): v_eqs.append ((v_i, 'Const')) continue vs_j = [v_j for v_j in rvs if knowledge.v_ids[v_j][0] == k] if endorsed: vs_j = [v_j for v_j in vs_j if eq_known (knowledge, (v_i, v_j))] if not vs_j: return None v_j = vs_j[0] v_eqs.append ((v_i, v_j)) return v_eqs
def mk_pairing_v_eqs (knowledge, pair): (_, _, (pairs, vs), _) = knowledge v_eqs = [] (lvs, rvs) = pairs[pair] zero = mk_word32 (0) for v_i in lvs: (k, const) = vs[v_i] if const and v_i[0] != zero: if eq_known (knowledge, (v_i, 'Const')): v_eqs.append ((v_i, 'Const')) continue vs_j = [v_j for v_j in rvs if vs[v_j][0] == k] vs_j = [v_j for v_j in vs_j if eq_known (knowledge, (v_i, v_j))] if not vs_j: return None if v_i[0] == zero: continue v_j = vs_j[0] v_eqs.append ((v_i, v_j)) return v_eqs
def mk_loop_counter_eq_hyp(p, split, restrs, n): details = (split, (0, 1), []) (tag, _) = p.node_tags[split] visit = split_visit_one_visit(tag, details, restrs, vc_offs(0)) return eq_hyp((mk_var('%n', word32T), visit), (mk_word32(n), visit), (split, 0))
def get_loop_vars_at (p, n): vs = [var for (var, data) in get_loop_var_analysis_at (p, n) if data == 'LoopVariable'] + [mk_word32 (0)] vs.sort () return vs
(m, p, v) = xs p_al = p.val & -4 shift = (p.val & 3) * 8 (naming, eqs) = m eqs = dict (eqs) prev_v = eqs[p_al] mask_v = prev_v & (((1 << 32) - 1) ^ (255 << shift)) new_v = mask_v | ((v.val & 255) << shift) eqs[p.val] = new_v eqs = tuple (sorted (eqs.items ())) result = (naming, eqs) elif op[0] == 'load-word32': (m, p) = xs (naming, eqs) = m eqs = dict (eqs) result = syntax.mk_word32 (eqs[p.val]) elif op[0] == 'load-word8': (m, p) = xs p_al = p.val & -4 shift = (p.val & 3) * 8 (naming, eqs) = m eqs = dict (eqs) v = (eqs[p_al] >> shift) & 255 result = syntax.mk_word8 (v) elif xs and xs[0].typ.kind == 'Word' and op in word_ops: for x in xs: assert x.kind == 'Num', (s, op, x) result = word_ops[op](* [x.val for x in xs]) result = result & ((1 << xs[0].typ.num) - 1) result = Expr ('Num', xs[0].typ, val = result) elif xs and xs[0].typ.kind == 'Word' and op in word_ineq_ops: