[文档]classSwitchDefaultCaseDuplicator(OptimizationPass):""" For each switch-case construct (identified by jump tables), duplicate the default-case node when we detect situations where the default-case node is seemingly reused by edges outside the switch-case construct. This code reuse is usually caused by compiler code deduplication. Ideally this pass should be implemented as an ISC optimization reversion. """ARCHES=NonePLATFORMS=NoneSTAGE=OptimizationPassStage.AFTER_AIL_GRAPH_CREATIONNAME="Duplicate default-case nodes to undo default-case node reuse caused by compiler code deduplication"DESCRIPTION=__doc__.strip()
def_check(self):jumptables=self.kb.cfgs.get_most_accurate().jump_tablesswitch_jump_block_addrs={jumptable.addrforjumptableinjumptables.values()ifjumptable.typein{IndirectJumpType.Jumptable_AddressComputed,IndirectJumpType.Jumptable_AddressLoadedFromMemory}}jump_node_addrs=self._func.block_addrs_set.intersection(switch_jump_block_addrs)ifnotjump_node_addrs:returnFalse,Nonedefault_case_node_addrs=set()fornode_addrinjump_node_addrs:node=self._func.get_node(node_addr)ifself._func.graph.in_degree[node]==1:pred=next(iter(self._func.graph.predecessors(node)))ifself._func.graph.out_degree[pred]==2:default_case_node=next(iter(nnfornninself._func.graph.successors(pred)ifnn.addr!=node_addr))ifself._func.graph.out_degree[default_case_node]==1:default_case_node_addrs.add((pred.addr,node_addr,default_case_node.addr))ifnotdefault_case_node_addrs:returnFalse,Nonecache={"default_case_node_addrs":default_case_node_addrs}returnTrue,cachedef_analyze(self,cache=None):default_case_node_addrs=cache["default_case_node_addrs"]out_graph=Noneduplicated_default_addrs:set[int]=set()default_addr_count=defaultdict(int)goto_rewritten_default_addrs=set()for_,_,default_addrindefault_case_node_addrs:default_addr_count[default_addr]+=1fordefault_addr,cntindefault_addr_count.items():ifcnt>1:# rewrite all of them into gotosdefault_node=self._get_block(default_addr)forswitch_head_addrinsorted((saforsa,_,daindefault_case_node_addrsifda==default_addr)):switch_head_node=self._get_block(switch_head_addr)goto_stmt=Jump(None,Const(None,None,default_addr,self.project.arch.bits,ins_addr=default_addr),target_idx=None,# I'm assuming the ID of the default node is None hereins_addr=default_addr,)goto_node=Block(default_addr,0,statements=[goto_stmt],idx=next(self.node_idx),)ifout_graphisNone:out_graph=self._graphout_graph.remove_edge(switch_head_node,default_node)out_graph.add_edge(switch_head_node,goto_node)out_graph.add_edge(goto_node,default_node)goto_rewritten_default_addrs.add(default_addr)forswitch_head_addr,jump_node_addr,default_addrindefault_case_node_addrs:ifdefault_addrinduplicated_default_addrsordefault_addringoto_rewritten_default_addrs:continuedefault_case_node=self._func.get_node(default_addr)unexpected_pred_addrs={pred.addrforpredinself._func.graph.predecessors(default_case_node)ifpred.addrnotin{switch_head_addr,jump_node_addr}}ifunexpected_pred_addrs:default_case_block=self._get_block(default_addr)default_case_succ_block=next(iter(self._graph.successors(default_case_block)))jump_nodes=self._get_blocks(jump_node_addr)jump_node_descedents=set()forjump_nodeinjump_nodes:jump_node_descedents|=networkx.descendants(self._graph,jump_node)duplicated_default_addrs.add(default_addr)# duplicate default_case_node for each unexpected predecessorforunexpected_pred_addrinunexpected_pred_addrs:forunexpected_predinself._get_blocks(unexpected_pred_addr):# is this predecessor reachable from the jump node? if so, we believe this is a legitimate edge# and do not duplicate it.ifunexpected_predinjump_node_descedents:continuedefault_case_block_copy=default_case_block.copy()default_case_block_copy.idx=next(self.node_idx)ifout_graphisNone:out_graph=self._graphout_graph.remove_edge(unexpected_pred,default_case_block)out_graph.add_edge(unexpected_pred,default_case_block_copy)out_graph.add_edge(default_case_block_copy,default_case_succ_block)self.out_graph=out_graph