def test_boost_example(self): # Graph taken from Figure 1 of # http://www.boost.org/doc/libs/1_56_0/libs/graph/doc/lengauer_tarjan_dominator.htm edges = [(0, 1), (1, 2), (1, 3), (2, 7), (3, 4), (4, 5), (4, 6), (5, 7), (6, 4)] G = nx.DiGraph(edges) assert (nx.immediate_dominators(G, 0) == { 0: 0, 1: 0, 2: 1, 3: 1, 4: 3, 5: 4, 6: 4, 7: 1 }) # Test postdominance. with nx.utils.reversed(G): assert (nx.immediate_dominators(G, 7) == { 0: 1, 1: 7, 2: 7, 3: 4, 4: 5, 5: 7, 6: 4, 7: 7 })
def test_domrel_png(self): # Graph taken from https://commons.wikipedia.org/wiki/File:Domrel.png edges = [(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)] G = nx.DiGraph(edges) result = nx.immediate_dominators(G, 1) assert result == {1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 2} # Test postdominance. result = nx.immediate_dominators(G.reverse(copy=False), 6) assert result == {1: 2, 2: 6, 3: 5, 4: 5, 5: 2, 6: 6}
def test_domrel_png(self): # Graph taken from https://commons.wikipedia.org/wiki/File:Domrel.png edges = [(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)] G = nx.DiGraph(edges) assert_equal(nx.immediate_dominators(G, 1), {1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 2}) # Test postdominance. with nx.utils.reversed(G): assert_equal(nx.immediate_dominators(G, 6), {1: 2, 2: 6, 3: 5, 4: 5, 5: 2, 6: 6})
def create_graph_from(self, starting_ep): visited = set() to_visit = [starting_ep] graph = nx.DiGraph() g = Digraph('G', filename='CFG.gv') count = 0 while len(to_visit) > 0: count += 1 current_ep = to_visit.pop(0) current_instr = self.program.get_instruction_at_execution_point( current_ep) for succ_ep in current_instr.get_successors_checked(): graph.add_edge(current_ep, succ_ep) g.edge(str(hex(current_instr.address)), str( hex( self.program.get_instruction_at_execution_point( succ_ep).address)), label=None) #g.edge(current_instr, self.program.get_instruction_at_execution_point(succ_ep),label=None) if succ_ep not in visited: visited.add(succ_ep) to_visit.append(succ_ep) g.view() self.program.possible_exit_points = [ ep for ep in graph if graph.out_degree(ep) == 0 ] if len(self.program.possible_exit_points ) > 0 and self.program.exit_point is None: self.program.exit_point = self.program.possible_exit_points[-1] imm_doms = nx.immediate_dominators(graph, starting_ep) graph_reverse = graph.reverse() post_doms = [ nx.immediate_dominators(graph_reverse, exit_ep) for exit_ep in self.program.possible_exit_points ] for ep in graph: instr = self.program.get_instruction_at_execution_point(ep) possible_post_dominators = {pd.get(ep, None) for pd in post_doms} if len(possible_post_dominators) == 1: instr.immediate_post_dominator = possible_post_dominators.pop() instr.predecessors = graph.predecessors(ep) instr.immediate_dominator = imm_doms[ep] return graph
def test_boost_example(self): # Graph taken from Figure 1 of # http://www.boost.org/doc/libs/1_56_0/libs/graph/doc/lengauer_tarjan_dominator.htm edges = [(0, 1), (1, 2), (1, 3), (2, 7), (3, 4), (4, 5), (4, 6), (5, 7), (6, 4)] G = nx.DiGraph(edges) assert_equal(nx.immediate_dominators(G, 0), {0: 0, 1: 0, 2: 1, 3: 1, 4: 3, 5: 4, 6: 4, 7: 1}) # Test postdominance. with nx.utils.reversed(G): assert_equal(nx.immediate_dominators(G, 7), {0: 1, 1: 7, 2: 7, 3: 4, 4: 5, 5: 7, 6: 4, 7: 7})
def _execute_graph(data): log("executing graph") controlflow = _get_required_controlflow_graph(data) splits = [] for n, idom in nx.immediate_dominators(controlflow, root_op).items(): if n == idom: continue if n in controlflow.successors(idom): continue splits.append((idom, n)) # Graph mode order = list(nx.topological_sort(controlflow)) iteration = 0 max_threads = 4 while iteration < len(order): ops = [] for i in range(max_threads): if iteration >= len(order): break next_op = order[iteration] if sum([op in nx.ancestors(controlflow, next_op) for op in ops]) > 0: break ops.append(order[iteration]) iteration += 1 _run_ops_in_parallel(ops)
def test_unreachable(self): n = 5 assert n > 1 G = nx.path_graph(n, create_using=nx.DiGraph()) assert (nx.immediate_dominators( G, n // 2) == {i: max(i - 1, n // 2) for i in range(n // 2, n)})
def acyclic_dominance_frontier(sdfg: SDFG, idom=None) -> Dict[SDFGState, Set[SDFGState]]: """ Finds the dominance frontier for an SDFG while ignoring any back edges. This is a modified version of the dominance frontiers algorithm as implemented by networkx. :param sdfg: The SDFG for which to compute the acyclic dominance frontier. :param idom: Optional precomputed immediate dominators. :return: A dictionary keyed by states, containing the dominance frontier for each SDFG state. """ idom = idom or nx.immediate_dominators(sdfg.nx, sdfg.start_state) dom_frontiers = {state: set() for state in sdfg.nodes()} for u in idom: if len(sdfg.nx.pred[u]) >= 2: for v in sdfg.nx.pred[u]: if v in idom: df_candidates = set() while v != idom[u]: if v == u: df_candidates = None break df_candidates.add(v) v = idom[v] if df_candidates is not None: for candidate in df_candidates: dom_frontiers[candidate].add(u) return dom_frontiers
def buildPdomTree(entryID, exitID, funName, CFGsR): '''build post domains tree Construct the postdominator tree for AugCFG :param entryID: :param exitID: :param funName: :param CFGsR: :return: ''' # Augment the CFG by adding a node Start with edge (Start, entry) # labeled “T” and edge (Start, exit) labeled “F”;call this AugCFG CFGsR.add_edges_from([(entryID, funName), (exitID, funName)]) nxPdomDict = nx.immediate_dominators(CFGsR, exitID) # PdomEdgeDict = dict() PdomEdges = list() for i in nxPdomDict: # if getNodeIDNo(nxPdomDict[i],CFGsR) not in PdomEdgeDict: # PdomEdgeDict[getNodeIDNo(nxPdomDict[i],CFGsR)] = list() if (nxPdomDict[i] != i): # PdomEdgeDict[getNodeIDNo(nxPdomDict[i],CFGsR)].append(getNodeIDNo(i,CFGsR)) PdomEdges.append((nxPdomDict[i], i)) # print(PdomEdgeDict) PdomTree = nx.DiGraph() PdomTree.add_edges_from(PdomEdges) return PdomTree
def dominate(name): for (n, _) in find_nodes(name): lst = dict((nx.immediate_dominators(G, n).items())) out.write(str(lst)) print(len(lst)) for (t, _) in targets: domination = [] temp = [t] while not (temp.__contains__(n)): list_p = [] for tt in temp: shortest = nx.dijkstra_path_length(G, lst[tt], tt) print("shortest:" + str(shortest)) if shortest > 3: parent = list(G.predecessors(tt)) print(parent) for p in parent: if p in lst.keys(): # shortest = nx.dijkstra_path_length(G, lst[p], tt) # print("shortest:" + str(shortest)) if nx.dijkstra_path_length(G, lst[p], tt) <= 3: list_p.append(lst[p]) else: list_p.append(lst[tt]) list_p = list(set(list_p)) temp = list_p list_n = [] for l in list_p: node = G.nodes[l]['label'] node = re.sub(r'[^\w\s]', '', node) print(node) list_n.append(node) domination.append(list_n) print(domination)
def _build_dom_sets(self, graph: nx.DiGraph, gname=''): v_entry = 'virtual_entry' graph.add_edges_from( ((v_entry, node) for node, i in tuple(graph.in_degree) if i == 0)) if v_entry not in graph: err_msg = f'Failed to find an entry to build dominance tree for {gname}' logging.error(err_msg) raise NoEntryForDomTreeError(err_msg) dom_tree = nx.DiGraph() dom_tree.add_nodes_from(graph.nodes) for node, dominator in nx.immediate_dominators(graph, v_entry).items(): dom_tree.add_edge(dominator, node) dom_tree.remove_node(v_entry) dominances = { node: set.union(nx.descendants(dom_tree, node), { node, }) for node in dom_tree.nodes } frontier = nx.dominance_frontiers(graph, v_entry) frontier.pop(v_entry) graph.remove_node(v_entry) return dom_tree, dominances, frontier
def test_irreducible1(self): # Graph taken from Figure 2 of # K. D. Cooper, T. J. Harvey, and K. Kennedy. # A simple, fast dominance algorithm. # Software Practice & Experience, 4:110, 2001. edges = [(1, 2), (2, 1), (3, 2), (4, 1), (5, 3), (5, 4)] G = nx.DiGraph(edges) assert (nx.immediate_dominators(G, 5) == {i: 5 for i in range(1, 6)})
def get_fixed_strings(r): # convert regex to graph G = nx.MultiDiGraph() cmd = ["./src/tools/util/regex2dfa", "-r", r] s = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True).stdout.decode("utf-8") end = None for l in s.splitlines(): items = l.split("\t") if len(items) == 4: a, b, i, o = map(int, items) G.add_edge(a, b, label=(i).to_bytes(length=1, byteorder="big")) else: assert len(items) == 1 end = int(items[0]) break assert end is not None # convenciente function def get_edges(u): ret = [] for v in G[u]: for i in G[u][v]: ret.append((u, v, G[u][v][i]["label"])) return ret # get relevant nodes start = 0 dom = nx.immediate_dominators(G, start) relevant = [end] node = end while node != start: node = dom[node] relevant.append(node) relevant = relevant[::-1] # get fixed strings starting from relevant nodes # idea: check if a node has only one edge, collect the label and continue # until a branch is discovered strs = {} seen = set() for node in relevant: strs[node] = [] edges = get_edges(node) while len(edges) == 1: u, v, l = edges[0] if u in seen or u == end: break seen.add(u) strs[node].append(l) edges = get_edges(v) return list( set([ b"".join(l) for l in sorted(strs.values(), key=len, reverse=True) if len(l) > 0 ]))
def dominator_forest(self): if self._dominator_forest is not None: return self._dominator_forest self._dominator_forest = DAG() for root in self.roots: for node, dominated_by in nx.immediate_dominators(self, root).items(): if node != dominated_by: self._dominator_forest.add_edge(dominated_by, node) return self._dominator_forest
def test_irreducible1(self): # Graph taken from Figure 2 of # K. D. Cooper, T. J. Harvey, and K. Kennedy. # A simple, fast dominance algorithm. # Software Practice & Experience, 4:110, 2001. edges = [(1, 2), (2, 1), (3, 2), (4, 1), (5, 3), (5, 4)] G = nx.DiGraph(edges) assert_equal(nx.immediate_dominators(G, 5), {i: 5 for i in range(1, 6)})
def _build_dom_tree(self, digraph, entry): if len(digraph.nodes) == 1: self._domTree.add_node(entry) return # Code below assumes digraph has more then one node idom_list = nx.immediate_dominators(digraph, entry).items() for tup in idom_list: if tup[0] == entry: continue self._domTree.add_edge(tup[1], tup[0])
def test_irreducible2(self): # Graph taken from Figure 4 of # K. D. Cooper, T. J. Harvey, and K. Kennedy. # A simple, fast dominance algorithm. # Software Practice & Experience, 4:110, 2001. edges = [(1, 2), (2, 1), (2, 3), (3, 2), (4, 2), (4, 3), (5, 1), (6, 4), (6, 5)] G = nx.DiGraph(edges) assert_equal(nx.immediate_dominators(G, 6), dict((i, 6) for i in range(1, 7)))
def _compute_dominators(self): import networkx g = networkx.DiGraph() for bb in self.bbs: for succ in bb.succ: g.add_edge(bb.start, succ.start) self._dominators = { self._bb_at[k]: self._bb_at[v] for k, v in networkx.immediate_dominators(g, 0).items() }
def dependencies(g, entry): dominators = nx.immediate_dominators(g, entry) deps = [] for n in g.nodes(): for m in g.nodes(): ifdom = dominators[m] if any([ifdom not in path for path in nx.all_simple_paths(g, n, m)]): deps.append((m, n)) return deps
def dominance_frontiers(G, start): """Returns the dominance frontiers of all nodes of a directed graph. Parameters ---------- G : a DiGraph or MultiDiGraph The graph where dominance is to be computed. start : node The start node of dominance computation. Returns ------- df : dict keyed by nodes A dict containing the dominance frontiers of each node reachable from ``start`` as lists. Raises ------ NetworkXNotImplemented If ``G`` is undirected. NetworkXError If ``start`` is not in ``G``. Examples -------- >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)]) >>> sorted((u, sorted(df)) for u, df in nx.dominance_frontiers(G, 1).items()) [(1, []), (2, [5]), (3, [5]), (4, [5]), (5, [])] References ---------- .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy. A simple, fast dominance algorithm. Software Practice & Experience, 4:110, 2001. """ idom = nx.immediate_dominators(G, start) df = {u: [] for u in idom} for u in idom: if len(G.pred[u]) - int(u in G.pred[u]) >= 2: p = set() for v in G.pred[u]: while v != idom[u] and v not in p: p.add(v) v = idom[v] p.discard(u) for v in p: df[v].append(u) return df
def dominance_frontiers(G, start): """Returns the dominance frontiers of all nodes of a directed graph. Parameters ---------- G : a DiGraph or MultiDiGraph The graph where dominance is to be computed. start : node The start node of dominance computation. Returns ------- df : dict keyed by nodes A dict containing the dominance frontiers of each node reachable from `start` as lists. Raises ------ NetworkXNotImplemented If `G` is undirected. NetworkXError If `start` is not in `G`. Examples -------- >>> G = nx.DiGraph([(1, 2), (1, 3), (2, 5), (3, 4), (4, 5)]) >>> sorted((u, sorted(df)) for u, df in nx.dominance_frontiers(G, 1).items()) [(1, []), (2, [5]), (3, [5]), (4, [5]), (5, [])] References ---------- .. [1] K. D. Cooper, T. J. Harvey, and K. Kennedy. A simple, fast dominance algorithm. Software Practice & Experience, 4:110, 2001. """ idom = nx.immediate_dominators(G, start) df = {u: [] for u in idom} for u in idom: if len(G.pred[u]) - int(u in G.pred[u]) >= 2: p = set() for v in G.pred[u]: while v != idom[u] and v not in p: p.add(v) v = idom[v] p.discard(u) for v in p: df[v].append(u) return df
def _post_dominate(reversed_graph, n1, n2): """ Checks whether `n1` post-dominates `n2` in the *original* (not reversed) graph. :param networkx.DiGraph reversed_graph: The reversed networkx.DiGraph instance. :param networkx.Node n1: Node 1. :param networkx.Node n2: Node 2. :returns bool: True/False. """ ds = networkx.immediate_dominators(reversed_graph, n1) return n2 in ds
def test_domrel_png(self): # Graph taken from https://commons.wikipedia.org/wiki/File:Domrel.png edges = [(1, 2), (2, 3), (2, 4), (2, 6), (3, 5), (4, 5), (5, 2)] G = nx.DiGraph(edges) assert (nx.immediate_dominators(G, 1) == { 1: 1, 2: 1, 3: 2, 4: 2, 5: 2, 6: 2 }) # Test postdominance. with nx.utils.reversed(G): assert (nx.immediate_dominators(G, 6) == { 1: 2, 2: 6, 3: 5, 4: 5, 5: 2, 6: 6 })
def nx_dominators(G, node_target, node_start='b0'): # get dominator tree, parent is immediate dominator lookup = nx.immediate_dominators(G, node_start) assert node_target in lookup # walk up the tree result = [] current = node_target while True: result.append(current) if current == node_start: break current = lookup[current] return result
def all_dominators(sdfg: SDFG, idom: Dict[SDFGState, SDFGState] = None) -> Dict[SDFGState, Set[SDFGState]]: """ Returns a mapping between each state and all its dominators. """ idom = idom or nx.immediate_dominators(sdfg.nx, sdfg.start_state) # Create a dictionary of all dominators of each node by using the # transitive closure of the DAG induced by the idoms g = nx.DiGraph() for node, dom in idom.items(): if node is dom: # Skip root continue g.add_edge(node, dom) tc = nx.transitive_closure_dag(g) alldoms: Dict[SDFGState, Set[SDFGState]] = {sdfg.start_state: set()} for node in tc: alldoms[node] = set(dst for _, dst in tc.out_edges(node)) return alldoms
def dom(p): g = nx.DiGraph(p.cfg_edges) d = nx.immediate_dominators(g, p.entry) roots = [node for node, idom in d.items() if node == idom] gd = nx.DiGraph((idom, node) for node, idom in d.items() if node != idom) r, s = [], [] for root in roots: r.extend(nx.dfs_preorder_nodes(gd, root)) s.extend(nx.dfs_postorder_nodes(gd, root)) p = {b: i for i, b in enumerate(r)} q = {b: i for i, b in enumerate(s)} return TreeQueries(p, q), d
def _refine_loop(self, graph, head, initial_loop_nodes, initial_exit_nodes): refined_loop_nodes = initial_loop_nodes.copy() refined_exit_nodes = initial_exit_nodes.copy() idom = networkx.immediate_dominators(graph, self._start_node) n_new = refined_exit_nodes while len(refined_exit_nodes) > 1 and len(n_new) != 0: n_new = set() for n in list(refined_exit_nodes): if len(set(graph.predecessors(n)) - refined_loop_nodes) == 0: refined_loop_nodes.add(n) refined_exit_nodes.remove(n) for u in (set(graph.successors(n)) - refined_loop_nodes): if self._dominates(idom, head, n): n_new.add(u) refined_exit_nodes |= n_new return refined_loop_nodes, refined_exit_nodes
def build_dominators(self, root: str): """ This builds the requisite dominator structures: dominator tree, immediate dominators, and dominance frontier for each function present in the program. :param root: Name of function to build for. :return: None """ self.frontier[root] = nx.dominance_frontiers( self.program.bb_graph, self.program.functions[root]['entry']) self.idoms[root] = nx.immediate_dominators( self.program.bb_graph, self.program.functions[root]['entry']) self.dominator_tree[root] = defaultdict(lambda: set()) for key, value in self.idoms[root].items(): # Ensure a self dominated node isn't added # This guarantees no infinite iteration if key != value: self.dominator_tree[root][value].add(key)
def compare_immediate_dominators(self, start, end, edges): G = nx.DiGraph(edges) idom_nx = sorted(nx.immediate_dominators(G, start).items()) cfg = ControlFlowGraph.fromEdgeList(start, end, edges) dom = dominators(cfg) idom_hw = immediateDominator(dom) # print(idom_nx) # print(idom_hw) for node, idom in idom_nx: if node == start: self.assertEqual(idom_hw[node], '') else: self.assertEqual( idom_hw[node], idom, "nx: {}, homework: {}".format(idom_nx, idom_hw))
def _refine_loop(self, graph, head, initial_loop_nodes, initial_exit_nodes): refined_loop_nodes = initial_loop_nodes.copy() refined_exit_nodes = initial_exit_nodes.copy() idom = networkx.immediate_dominators(graph, self._start_node) new_exit_nodes = refined_exit_nodes while len(refined_exit_nodes) > 1 and new_exit_nodes: new_exit_nodes = set() for n in list(refined_exit_nodes): if all(pred in refined_loop_nodes for pred in graph.predecessors(n)) and dominates(idom, head, n): refined_loop_nodes.add(n) refined_exit_nodes.remove(n) for u in (set(graph.successors(n)) - refined_loop_nodes): new_exit_nodes.add(u) refined_exit_nodes |= new_exit_nodes refined_loop_nodes = refined_loop_nodes - refined_exit_nodes return refined_loop_nodes, refined_exit_nodes
def _update_stable(self): # compute stable vertices self.log("""== Computing the stable vertices ==""") # compute immediate dominators self.dominators = networkx.immediate_dominators(self.N, self.root) # for each vertex, assign a leaf that they are stable on self.stability = dict((u, u) for u in self.leaves) X = list(self.leaves.copy()) while X: u = X.pop() v = self.dominators[u] if v not in self.stability: self.stability[v] = u X.append(v) if self.verbose: self.log('immediate dominators:') for u in self.stability: if self.is_stable(u): self.log(str(u) + ': ' + str(self.stability[u]))
def get_dominator_graphs(self): assert self.forwarding_graphs, "Forwarding Graph needs to be built before the Dominator Graphs" for subnet, forwarding_graph in self.forwarding_graphs.items(): rev_graph = forwarding_graph.reverse(copy=True) try: dominators = set( nx.immediate_dominators(rev_graph, 'sink').items()) except nx.NetworkXError as e: self.logger.error( "Sink node is not in the forwarding graph for {subnet}. " "Official error message: {error}".format(subnet=subnet, error=e)) # sys.exit(1) else: if ("sink", "sink") in dominators: dominators.remove(("sink", "sink")) tmp_dominator_graph = nx.DiGraph(list(dominators)) self.dominator_graphs[subnet] = tmp_dominator_graph return self.dominator_graphs
edges = [] with open('edges.txt') as fedges: for line in fedges: e = line.split() edges.append((int(e[0]),int(e[1]))) # creating the graph from edges.txt graph=nx.DiGraph() graph.add_nodes_from(IDs) graph.add_edges_from(edges) dftree = nx.dfs_tree(graph,0) #tree = nx.dfs_edges(graph) imdom = sorted(nx.immediate_dominators(graph, 0).items()) # immediante dominance dom_tree = nx.DiGraph() #dom_tree.add_nodes_from(IDs) dom_tree.add_edges_from(imdom) fdomtree = open("domtree.txt",'w') fdomtree.write('\n'.join('%s %s' % (str(x[0]), str(x[1])) for x in dftree.edges())) fdomtree.close() #nx.draw_graphviz(dftree, with_labels = True) # drawings # nx.draw_networkx(dom_tree, with_labels = True) #nx.draw(graph)
def recover_reaching_conditions(self, region, with_successors=False, jump_tables=None): def _strictly_postdominates(inv_idoms, node_a, node_b): """ Does node A strictly post-dominate node B on the graph? """ return dominates(inv_idoms, node_a, node_b) edge_conditions = {} predicate_mapping = {} # traverse the graph to recover the condition for each edge for src in region.graph.nodes(): nodes = list(region.graph[src]) if len(nodes) >= 1: for dst in nodes: edge = src, dst edge_data = region.graph.get_edge_data(*edge) edge_type = edge_data.get('type', 'transition') try: predicate = self._extract_predicate( src, dst, edge_type) except EmptyBlockNotice: # catch empty block notice - although this should not really happen predicate = claripy.true edge_conditions[edge] = predicate predicate_mapping[predicate] = dst if jump_tables: self.recover_reaching_conditions_for_jumptables( region, jump_tables, edge_conditions) if with_successors and region.graph_with_successors is not None: _g = region.graph_with_successors else: _g = region.graph end_nodes = {n for n in _g.nodes() if _g.out_degree(n) == 0} inverted_graph = shallow_reverse(_g) if end_nodes: if len(end_nodes) > 1: # make sure there is only one end node dummy_node = "DUMMY_NODE" for end_node in end_nodes: inverted_graph.add_edge(dummy_node, end_node) endnode = dummy_node else: endnode = next(iter(end_nodes)) # pick the end node idoms = networkx.immediate_dominators(inverted_graph, endnode) else: idoms = None reaching_conditions = {} # recover the reaching condition for each node sorted_nodes = CFGUtils.quasi_topological_sort_nodes(_g) terminating_nodes = [] for node in sorted_nodes: preds = _g.predecessors(node) reaching_condition = None out_degree = _g.out_degree(node) if out_degree == 0: terminating_nodes.append(node) if node is region.head: # the head is always reachable reaching_condition = claripy.true elif idoms is not None and _strictly_postdominates( idoms, node, region.head): # the node that post dominates the head is always reachable reaching_conditions[node] = claripy.true else: for pred in preds: edge = (pred, node) pred_condition = reaching_conditions.get( pred, claripy.true) edge_condition = edge_conditions.get(edge, claripy.true) if reaching_condition is None: reaching_condition = claripy.And( pred_condition, edge_condition) else: reaching_condition = claripy.Or( claripy.And(pred_condition, edge_condition), reaching_condition) if reaching_condition is not None: reaching_conditions[node] = self.simplify_condition( reaching_condition) # My hypothesis to be proved: in any regioned graph, there must be a node with a 0 out-degree whose reaching # conditions can be marked as True. In other words, if all 0 out-degree nodes have non-trivial reaching # conditions, we can always pick one of them and change its reaching condition to True, without changing the # semantics of the regioned graph. if terminating_nodes and all(not reaching_conditions[node].is_true() for node in terminating_nodes if node in reaching_conditions): # pick the node with the greatest in-degree terminating_nodes = sorted(terminating_nodes, key=_g.in_degree) node_with_greatest_indegree = terminating_nodes[-1] if _g.in_degree(node_with_greatest_indegree) > 1: # forcing the in-degree to be greater than 1 allows us to skip the case blocks in switch-cases # otherwise structurer will fail to structure the control flow reaching_conditions[node_with_greatest_indegree] = claripy.true l.warning( "Marking node %r as trivially reachable. Disable this optimization in condition_processor.py " "if it leads to incorrect decompilation result.", node_with_greatest_indegree) # Another hypothesis: for nodes where two paths come together *and* those that cannot be further structured into # another if-else construct (we take the short-cut by testing if the operator is an "Or" after running our # condition simplifiers previously), we are better off using their "guarding conditions" instead of their # reaching conditions for if-else. see my super long chatlog with rhelmot on 5/14/2021. guarding_conditions = {} for the_node in sorted_nodes: preds = list(_g.predecessors(the_node)) if len(preds) != 2: continue # generate a graph slice that goes from the region head to this node slice_nodes = list(networkx.dfs_tree(inverted_graph, the_node)) subgraph = networkx.subgraph(_g, slice_nodes) # figure out which paths cause the divergence from this node nodes_do_not_reach_the_node = set() for node_ in subgraph: if node_ is the_node: continue for succ in _g.successors(node_): if not networkx.has_path(_g, succ, the_node): nodes_do_not_reach_the_node.add(succ) diverging_conditions = [] for node_ in nodes_do_not_reach_the_node: preds_ = list(_g.predecessors(node_)) for pred_ in preds_: if pred_ in nodes_do_not_reach_the_node: continue # this predecessor is the diverging node! edge_ = pred_, node_ edge_condition = edge_conditions.get(edge_, None) if edge_condition is not None: diverging_conditions.append(edge_condition) if diverging_conditions: # the negation of the union of diverging conditions is the guarding condition for this node cond = claripy.Or(*map(claripy.Not, diverging_conditions)) guarding_conditions[the_node] = cond self.reaching_conditions = reaching_conditions self.guarding_conditions = guarding_conditions
def test_cycle(self): n = 5 G = nx.cycle_graph(n, create_using=nx.DiGraph()) assert_equal(nx.immediate_dominators(G, 0), {i: max(i - 1, 0) for i in range(n)})
def test_unreachable(self): n = 5 assert_greater(n, 1) G = nx.path_graph(n, create_using=nx.DiGraph()) assert_equal(nx.immediate_dominators(G, n // 2), {i: max(i - 1, n // 2) for i in range(n // 2, n)})
def recover_reaching_conditions(self, region, with_successors=False, jump_tables=None): def _strictly_postdominates(inv_idoms, node_a, node_b): """ Does node A strictly post-dominate node B on the graph? """ return dominates(inv_idoms, node_a, node_b) edge_conditions = {} predicate_mapping = {} # traverse the graph to recover the condition for each edge for src in region.graph.nodes(): nodes = list(region.graph[src]) if len(nodes) >= 1: for dst in nodes: edge = src, dst edge_data = region.graph.get_edge_data(*edge) edge_type = edge_data.get('type', 'transition') try: predicate = self._extract_predicate(src, dst, edge_type) except EmptyBlockNotice: # catch empty block notice - although this should not really happen predicate = claripy.true edge_conditions[edge] = predicate predicate_mapping[predicate] = dst if jump_tables: self.recover_reaching_conditions_for_jumptables(region, jump_tables, edge_conditions) if with_successors and region.graph_with_successors is not None: _g = region.graph_with_successors else: _g = region.graph end_nodes = {n for n in _g.nodes() if _g.out_degree(n) == 0} if end_nodes: inverted_graph = shallow_reverse(_g) if len(end_nodes) > 1: # make sure there is only one end node dummy_node = "DUMMY_NODE" for end_node in end_nodes: inverted_graph.add_edge(dummy_node, end_node) endnode = dummy_node else: endnode = next(iter(end_nodes)) # pick the end node idoms = networkx.immediate_dominators(inverted_graph, endnode) else: idoms = None reaching_conditions = {} # recover the reaching condition for each node sorted_nodes = CFGUtils.quasi_topological_sort_nodes(_g) terminating_nodes = [ ] for node in sorted_nodes: preds = _g.predecessors(node) reaching_condition = None out_degree = _g.out_degree(node) if out_degree == 0: terminating_nodes.append(node) if node is region.head: # the head is always reachable reaching_condition = claripy.true elif idoms is not None and _strictly_postdominates(idoms, node, region.head): # the node that post dominates the head is always reachable reaching_conditions[node] = claripy.true else: for pred in preds: edge = (pred, node) pred_condition = reaching_conditions.get(pred, claripy.true) edge_condition = edge_conditions.get(edge, claripy.true) if reaching_condition is None: reaching_condition = claripy.And(pred_condition, edge_condition) else: reaching_condition = claripy.Or(claripy.And(pred_condition, edge_condition), reaching_condition) if reaching_condition is not None: reaching_conditions[node] = self.simplify_condition(reaching_condition) # My hypothesis to be proved: in any regioned graph, there must be a node with a 0 out-degree whose reaching # conditions can be marked as True. In other words, if all 0 out-degree nodes have non-trivial reaching # conditions, we can always pick one of them and change its reaching condition to True, without changing the # semantics of the regioned graph. if terminating_nodes and all(not reaching_conditions[node].is_true() for node in terminating_nodes if node in reaching_conditions): # pick the node with the greatest in-degree terminating_nodes = sorted(terminating_nodes, key=_g.in_degree) node_with_greatest_indegree = terminating_nodes[-1] if _g.in_degree(node_with_greatest_indegree) > 1: # forcing the in-degree to be greater than 1 allows us to skip the case blocks in switch-cases # otherwise structurer will fail to structure the control flow reaching_conditions[node_with_greatest_indegree] = claripy.true l.warning("Marking node %r as trivially reachable. Disable this optimization in condition_processor.py " "if it leads to incorrect decompilation result.", node_with_greatest_indegree) self.reaching_conditions = reaching_conditions
def test_singleton(self): G = nx.DiGraph() G.add_node(0) assert_equal(nx.immediate_dominators(G, 0), {0: 0}) G.add_edge(0, 0) assert_equal(nx.immediate_dominators(G, 0), {0: 0})