(self, g, src, dst, data, blockaddr_to_function, known_functions, all_edges)
Graph traversal handler. It takes in a node or an edge, and create new functions or add nodes to existing
        functions accordingly. Oh, it also create edges on the transition map of functions.

        :param g:           The control flow graph that is currently being traversed.
        :param CFGNode src: Beginning of the edge, or a single node when dst is None.
        :param CFGNode dst: Destination of the edge. For processing a single node, `dst` is None.
        :param dict data: Edge data in the CFG. 'jumpkind' should be there if it's not None.
        :param dict blockaddr_to_function: A mapping between block addresses to Function instances.
        :param angr.knowledge_plugins.FunctionManager known_functions: Already recovered functions.
        :param list or None all_edges: All edges going out from src.
        :return: None
Graph traversal handler. It takes in a node or an edge, and create new functions or add nodes to existing
        functions accordingly. Oh, it also create edges on the transition map of functions.
Graph traversal handler . It takes in a node or an edge and create new functions or add nodes to existing functions accordingly . Oh it also create edges on the transition map of functions .
def _graph_traversal_handler(self, g, src, dst, data, blockaddr_to_function, known_functions, all_edges):
        """
        Graph traversal handler. It takes in a node or an edge, and create new functions or add nodes to existing
        functions accordingly. Oh, it also create edges on the transition map of functions.

        :param g:           The control flow graph that is currently being traversed.
        :param CFGNode src: Beginning of the edge, or a single node when dst is None.
        :param CFGNode dst: Destination of the edge. For processing a single node, `dst` is None.
        :param dict data: Edge data in the CFG. 'jumpkind' should be there if it's not None.
        :param dict blockaddr_to_function: A mapping between block addresses to Function instances.
        :param angr.knowledge_plugins.FunctionManager known_functions: Already recovered functions.
        :param list or None all_edges: All edges going out from src.
        :return: None
        """

        src_addr = src.addr
        src_function = self._addr_to_function(src_addr, blockaddr_to_function, known_functions)

        if src_addr not in src_function.block_addrs_set:
            n = self.model.get_any_node(src_addr)
            if n is None:
                node = src_addr
            else:
                node = self._to_snippet(n)
            self.kb.functions._add_node(src_function.addr, node)

        if data is None:
            # it's a single node only
            return

        jumpkind = data['jumpkind']

        if jumpkind == 'Ijk_Ret':
            n = self.model.get_any_node(src_addr)
            if n is None:
                from_node = src_addr
            else:
                from_node = self._to_snippet(n)
            self.kb.functions._add_return_from(src_function.addr, from_node, None)

        if dst is None:
            return

        dst_addr = dst.addr

        # get instruction address and statement index
        ins_addr = data.get('ins_addr', None)
        stmt_idx = data.get('stmt_idx', None)

        if jumpkind == 'Ijk_Call' or jumpkind.startswith('Ijk_Sys'):

            is_syscall = jumpkind.startswith('Ijk_Sys')

            # It must be calling a function
            dst_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)

            n = self.model.get_any_node(src_addr)
            if n is None:
                src_snippet = self._to_snippet(addr=src_addr, base_state=self._base_state)
            else:
                src_snippet = self._to_snippet(cfg_node=n)

            # HACK: FIXME: We need a better way of representing unresolved calls and whether they return.
            # For now, assume UnresolvedTarget returns if we're calling to it

            # If the function doesn't return, don't add a fakeret!
            if not all_edges or (dst_function.returning is False and not dst_function.name == 'UnresolvableCallTarget'):
                fakeret_node = None
            else:
                fakeret_node = self._one_fakeret_node(all_edges)

            if fakeret_node is None:
                fakeret_snippet = None
            else:
                fakeret_snippet = self._to_snippet(cfg_node=fakeret_node)

            if isinstance(dst_addr, SootAddressDescriptor):
                dst_addr = dst_addr.method

            self.kb.functions._add_call_to(src_function.addr, src_snippet, dst_addr, fakeret_snippet,
                                           syscall=is_syscall, ins_addr=ins_addr, stmt_idx=stmt_idx)

            if dst_function.returning:
                returning_target = src.addr + src.size
                if returning_target not in blockaddr_to_function:
                    if returning_target not in known_functions:
                        blockaddr_to_function[returning_target] = src_function
                    else:
                        self._addr_to_function(returning_target, blockaddr_to_function, known_functions)

                to_outside = not blockaddr_to_function[returning_target] is src_function

                n = self.model.get_any_node(returning_target)
                if n is None:
                    try:
                        returning_snippet = self._to_snippet(addr=returning_target, base_state=self._base_state)
                    except SimEngineError:
                        # it may not exist
                        returning_snippet = None
                else:
                    returning_snippet = self._to_snippet(cfg_node=n)

                if returning_snippet is not None:
                    self.kb.functions._add_fakeret_to(src_function.addr, src_snippet, returning_snippet, confirmed=True,
                                                      to_outside=to_outside
                                                      )

        elif jumpkind in ('Ijk_Boring', 'Ijk_InvalICache', 'Ijk_Exception'):

            # convert src_addr and dst_addr to CodeNodes
            n = self.model.get_any_node(src_addr)
            if n is None:
                src_node = src_addr
            else:
                src_node = self._to_snippet(cfg_node=n)

            n = self.model.get_any_node(dst_addr)
            if n is None:
                dst_node = dst_addr
            else:
                dst_node = self._to_snippet(cfg_node=n)

            # pre-check: if source and destination do not belong to the same section, it must be jumping to another
            # function
            belong_to_same_section = self._addrs_belong_to_same_section(src_addr, dst_addr)
            if not belong_to_same_section:
                _ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)

            if self._detect_tail_calls:
                if self._is_tail_call_optimization(g, src_addr, dst_addr, src_function, all_edges, known_functions,
                                                   blockaddr_to_function):
                    l.debug("Possible tail-call optimization detected at function %#x.", dst_addr)
                    # it's (probably) a tail-call optimization. we should make the destination node a new function
                    # instead.
                    blockaddr_to_function.pop(dst_addr, None)
                    _ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
                    self.kb.functions._add_outside_transition_to(src_function.addr, src_node, dst_node,
                                                                 to_function_addr=dst_addr
                                                                 )
                    self._tail_calls.add(dst_addr)

            # is it a jump to another function?
            if isinstance(dst_addr, SootAddressDescriptor):
                is_known_function_addr = dst_addr.method in known_functions and dst_addr.method.addr == dst_addr
            else:
                is_known_function_addr = dst_addr in known_functions

            if (is_known_function_addr and dst_addr != src_function.addr) or (
                dst_addr in blockaddr_to_function and blockaddr_to_function[dst_addr] is not src_function
            ):
                # yes it is
                dst_function_addr = blockaddr_to_function[dst_addr].addr if dst_addr in blockaddr_to_function else \
                    dst_addr

                self.kb.functions._add_outside_transition_to(src_function.addr, src_node, dst_node,
                                                             to_function_addr=dst_function_addr,
                                                             is_exception=jumpkind == 'Ijk_Exception',
                                                             )

                _ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
            else:
                # no it's not
                # add the transition code

                if dst_addr not in blockaddr_to_function:
                    blockaddr_to_function[dst_addr] = src_function

                self.kb.functions._add_transition_to(src_function.addr, src_node, dst_node, ins_addr=ins_addr,
                                                     stmt_idx=stmt_idx, is_exception=jumpkind == 'Ijk_Exception',
                                                     )

        elif jumpkind == 'Ijk_FakeRet':

            # convert src_addr and dst_addr to CodeNodes
            n = self.model.get_any_node(src_addr)
            if n is None:
                src_node = src_addr
            else:
                src_node = self._to_snippet(n)

            n = self.model.get_any_node(dst_addr)
            if n is None:
                dst_node = dst_addr
            else:
                dst_node = self._to_snippet(n)

            if dst_addr not in blockaddr_to_function:
                if isinstance(dst_addr, SootAddressDescriptor):
                    if dst_addr.method not in known_functions:
                        blockaddr_to_function[dst_addr] = src_function
                        target_function = src_function
                    else:
                        target_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
                else:
                    if dst_addr not in known_functions:
                        blockaddr_to_function[dst_addr] = src_function
                        target_function = src_function
                    else:
                        target_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
            else:
                target_function = blockaddr_to_function[dst_addr]

            # Figure out if the function called (not the function returned to) returns.
            # We may have determined that this does not happen, since the time this path
            # was scheduled for exploration
            called_function = None
            # Try to find the call that this fakeret goes with
            for _, d, e in all_edges:
                if e['jumpkind'] == 'Ijk_Call':
                    if d.addr in blockaddr_to_function:
                        called_function = blockaddr_to_function[d.addr]
                        break
            # We may have since figured out that the called function doesn't ret.
            # It's important to assume that all unresolved targets do return
            if called_function is not None and \
                    called_function.returning is False:
                return

            to_outside = not target_function is src_function

            # FIXME: Not sure we should confirm this fakeret or not.
            self.kb.functions._add_fakeret_to(src_function.addr, src_node, dst_node, confirmed=True,
                                              to_outside=to_outside, to_function_addr=target_function.addr
                                              )

        else:
            l.debug('Ignored jumpkind %s', jumpkind)
def _graph_traversal_handler ( self , g , src , dst , data , blockaddr_to_function , known_functions , all_edges ) : src_addr = src . addr src_function = self . _addr_to_function ( src_addr , blockaddr_to_function , known_functions ) if src_addr not in src_function . block_addrs_set : n = self . model . get_any_node ( src_addr ) if n is None : node = src_addr else : node = self . _to_snippet ( n ) self . kb . functions . _add_node ( src_function . addr , node ) if data is None : # it's a single node only return jumpkind = data [ 'jumpkind' ] if jumpkind == 'Ijk_Ret' : n = self . model . get_any_node ( src_addr ) if n is None : from_node = src_addr else : from_node = self . _to_snippet ( n ) self . kb . functions . _add_return_from ( src_function . addr , from_node , None ) if dst is None : return dst_addr = dst . addr # get instruction address and statement index ins_addr = data . get ( 'ins_addr' , None ) stmt_idx = data . get ( 'stmt_idx' , None ) if jumpkind == 'Ijk_Call' or jumpkind . startswith ( 'Ijk_Sys' ) : is_syscall = jumpkind . startswith ( 'Ijk_Sys' ) # It must be calling a function dst_function = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) n = self . model . get_any_node ( src_addr ) if n is None : src_snippet = self . _to_snippet ( addr = src_addr , base_state = self . _base_state ) else : src_snippet = self . _to_snippet ( cfg_node = n ) # HACK: FIXME: We need a better way of representing unresolved calls and whether they return. # For now, assume UnresolvedTarget returns if we're calling to it # If the function doesn't return, don't add a fakeret! if not all_edges or ( dst_function . returning is False and not dst_function . name == 'UnresolvableCallTarget' ) : fakeret_node = None else : fakeret_node = self . _one_fakeret_node ( all_edges ) if fakeret_node is None : fakeret_snippet = None else : fakeret_snippet = self . _to_snippet ( cfg_node = fakeret_node ) if isinstance ( dst_addr , SootAddressDescriptor ) : dst_addr = dst_addr . method self . kb . functions . _add_call_to ( src_function . addr , src_snippet , dst_addr , fakeret_snippet , syscall = is_syscall , ins_addr = ins_addr , stmt_idx = stmt_idx ) if dst_function . returning : returning_target = src . addr + src . size if returning_target not in blockaddr_to_function : if returning_target not in known_functions : blockaddr_to_function [ returning_target ] = src_function else : self . _addr_to_function ( returning_target , blockaddr_to_function , known_functions ) to_outside = not blockaddr_to_function [ returning_target ] is src_function n = self . model . get_any_node ( returning_target ) if n is None : try : returning_snippet = self . _to_snippet ( addr = returning_target , base_state = self . _base_state ) except SimEngineError : # it may not exist returning_snippet = None else : returning_snippet = self . _to_snippet ( cfg_node = n ) if returning_snippet is not None : self . kb . functions . _add_fakeret_to ( src_function . addr , src_snippet , returning_snippet , confirmed = True , to_outside = to_outside ) elif jumpkind in ( 'Ijk_Boring' , 'Ijk_InvalICache' , 'Ijk_Exception' ) : # convert src_addr and dst_addr to CodeNodes n = self . model . get_any_node ( src_addr ) if n is None : src_node = src_addr else : src_node = self . _to_snippet ( cfg_node = n ) n = self . model . get_any_node ( dst_addr ) if n is None : dst_node = dst_addr else : dst_node = self . _to_snippet ( cfg_node = n ) # pre-check: if source and destination do not belong to the same section, it must be jumping to another # function belong_to_same_section = self . _addrs_belong_to_same_section ( src_addr , dst_addr ) if not belong_to_same_section : _ = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) if self . _detect_tail_calls : if self . _is_tail_call_optimization ( g , src_addr , dst_addr , src_function , all_edges , known_functions , blockaddr_to_function ) : l . debug ( "Possible tail-call optimization detected at function %#x." , dst_addr ) # it's (probably) a tail-call optimization. we should make the destination node a new function # instead. blockaddr_to_function . pop ( dst_addr , None ) _ = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) self . kb . functions . _add_outside_transition_to ( src_function . addr , src_node , dst_node , to_function_addr = dst_addr ) self . _tail_calls . add ( dst_addr ) # is it a jump to another function? if isinstance ( dst_addr , SootAddressDescriptor ) : is_known_function_addr = dst_addr . method in known_functions and dst_addr . method . addr == dst_addr else : is_known_function_addr = dst_addr in known_functions if ( is_known_function_addr and dst_addr != src_function . addr ) or ( dst_addr in blockaddr_to_function and blockaddr_to_function [ dst_addr ] is not src_function ) : # yes it is dst_function_addr = blockaddr_to_function [ dst_addr ] . addr if dst_addr in blockaddr_to_function else dst_addr self . kb . functions . _add_outside_transition_to ( src_function . addr , src_node , dst_node , to_function_addr = dst_function_addr , is_exception = jumpkind == 'Ijk_Exception' , ) _ = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) else : # no it's not # add the transition code if dst_addr not in blockaddr_to_function : blockaddr_to_function [ dst_addr ] = src_function self . kb . functions . _add_transition_to ( src_function . addr , src_node , dst_node , ins_addr = ins_addr , stmt_idx = stmt_idx , is_exception = jumpkind == 'Ijk_Exception' , ) elif jumpkind == 'Ijk_FakeRet' : # convert src_addr and dst_addr to CodeNodes n = self . model . get_any_node ( src_addr ) if n is None : src_node = src_addr else : src_node = self . _to_snippet ( n ) n = self . model . get_any_node ( dst_addr ) if n is None : dst_node = dst_addr else : dst_node = self . _to_snippet ( n ) if dst_addr not in blockaddr_to_function : if isinstance ( dst_addr , SootAddressDescriptor ) : if dst_addr . method not in known_functions : blockaddr_to_function [ dst_addr ] = src_function target_function = src_function else : target_function = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) else : if dst_addr not in known_functions : blockaddr_to_function [ dst_addr ] = src_function target_function = src_function else : target_function = self . _addr_to_function ( dst_addr , blockaddr_to_function , known_functions ) else : target_function = blockaddr_to_function [ dst_addr ] # Figure out if the function called (not the function returned to) returns. # We may have determined that this does not happen, since the time this path # was scheduled for exploration called_function = None # Try to find the call that this fakeret goes with for _ , d , e in all_edges : if e [ 'jumpkind' ] == 'Ijk_Call' : if d . addr in blockaddr_to_function : called_function = blockaddr_to_function [ d . addr ] break # We may have since figured out that the called function doesn't ret. # It's important to assume that all unresolved targets do return if called_function is not None and called_function . returning is False : return to_outside = not target_function is src_function # FIXME: Not sure we should confirm this fakeret or not. self . kb . functions . _add_fakeret_to ( src_function . addr , src_node , dst_node , confirmed = True , to_outside = to_outside , to_function_addr = target_function . addr ) else : l . debug ( 'Ignored jumpkind %s' , jumpkind )