1
2import logging
3
4from archinfo.arch_soot import SootAddressDescriptor
5
6from ...errors import SimValueError, SimSolverModeError
7from ...state_plugins.callstack import CallStack
8
9l = logging.getLogger(name=__name__)
10
11# TODO: Make callsite an object and use it in BlockID and FunctionKey
12
13
14class BlockID(object):
15    """
16    A context-sensitive key for a SimRun object.
17    """
18
19    def __init__(self, addr, callsite_tuples, jump_type):
20        self.addr = addr
21        self.callsite_tuples = callsite_tuples
22        self.jump_type = jump_type
23
24        self._hash = None
25
26    def callsite_repr(self):
27
28        if self.callsite_tuples is None:
29            return "None"
30
31        s = [ ]
32        format_addr = lambda addr: 'None' if addr is None else hex(addr)
33        for i in range(0, len(self.callsite_tuples), 2):
34            s.append('@'.join(map(format_addr, self.callsite_tuples[i:i+2])))
35        return " -> ".join(s)
36
37    def __repr__(self):
38        return "<BlockID %#08x (%s) %% %s>" % (self.addr, self.callsite_repr(), self.jump_type)
39
40    def __hash__(self):
41        if self._hash is None:
42            self._hash = hash((self.callsite_tuples,) + (self.addr, self.jump_type))
43        return self._hash
44
45    def __eq__(self, other):
46        return isinstance(other, BlockID) and \
47               self.addr == other.addr and self.callsite_tuples == other.callsite_tuples and \
48               self.jump_type == other.jump_type
49
50    def __ne__(self, other):
51        return not self == other
52
53    @staticmethod
54    def new(addr, callstack_suffix, jumpkind):
55        if jumpkind.startswith('Ijk_Sys') or jumpkind == 'syscall':
56            jump_type = 'syscall'
57        elif jumpkind in ('Ijk_Exit', 'exit'):
58            jump_type = 'exit'
59        else:
60            jump_type = "normal"
61        return BlockID(addr, callstack_suffix, jump_type)
62
63    @property
64    def func_addr(self):
65        if self.callsite_tuples:
66            return self.callsite_tuples[-1]
67        return None
68
69
70class FunctionKey(object):
71    """
72    A context-sensitive key for a function.
73    """
74
75    def __init__(self, addr, callsite_tuples):
76        self.addr = addr
77        self.callsite_tuples = callsite_tuples
78
79        self._hash = None
80
81    def callsite_repr(self):
82
83        if self.callsite_tuples is None:
84            return "None"
85
86        s = []
87        format_addr = lambda addr: 'None' if addr is None else hex(addr)
88        for i in range(0, len(self.callsite_tuples), 2):
89            s.append('@'.join(map(format_addr, self.callsite_tuples[i:i + 2])))
90        return " -> ".join(s)
91
92    def __repr__(self):
93        s = "<FuncKey %#08x (%s)>" % (self.addr, self.callsite_repr())
94        return s
95
96    def __hash__(self):
97        if self._hash is None:
98            self._hash = hash((self.callsite_tuples, ) + (self.addr, ))
99        return self._hash
100
101    def __eq__(self, other):
102        return isinstance(other, FunctionKey) and \
103                self.addr == other.addr and self.callsite_tuples == other.callsite_tuples
104
105    @staticmethod
106    def new(addr, callsite_tuples):
107        return FunctionKey(addr, callsite_tuples)
108
109
110class CFGJobBase(object):
111    """
112    Describes an entry in CFG or VFG. Only used internally by the analysis.
113    """
114    def __init__(self, addr, state, context_sensitivity_level, block_id=None, src_block_id=None,
115                 src_exit_stmt_idx=None, src_ins_addr=None, jumpkind=None, call_stack=None, is_narrowing=False,
116                 skip=False, final_return_address=None):
117        self.addr = addr  # Note that addr may not always be equal to self.state.ip (for syscalls, for example)
118        self.state = state
119        self.jumpkind = jumpkind
120        self.src_block_id = src_block_id
121        self.src_exit_stmt_idx = src_exit_stmt_idx
122        self.src_ins_addr = src_ins_addr
123        self.skip = skip
124        self._block_id = block_id
125
126        # Other parameters
127        self._context_sensitivity_level = context_sensitivity_level
128        self.is_narrowing = is_narrowing
129
130        if call_stack is None:
131            self._call_stack = CallStack()
132
133            # Added the function address of the current exit to callstack
134            se = self.state.solver
135            sp_expr = self.state.regs.sp
136
137            # If the sp_expr cannot be concretized, the stack pointer cannot be traced anymore.
138            try:
139                sp = se.eval_one(sp_expr)
140            except (SimValueError, SimSolverModeError):
141                l.warning("Stack pointer cannot be concretized. CallStack cannot track the stack pointer changes.")
142
143                # Set the stack pointer to None
144                sp = None
145
146            self._call_stack = self._call_stack.call(None, self.addr, retn_target=final_return_address, stack_pointer=sp)
147
148        else:
149            self._call_stack = call_stack
150
151    @property
152    def call_stack(self):
153        return self._call_stack
154
155    def call_stack_copy(self):
156        return self._call_stack.copy()
157
158    def get_call_stack_suffix(self):
159        return self._call_stack.stack_suffix(self._context_sensitivity_level)
160
161    @property
162    def func_addr(self):
163        return self._call_stack.current_function_address
164
165    @func_addr.setter
166    def func_addr(self, v):
167        # Make a copy because we might be sharing it with other CFGJobs
168        self._call_stack = self._call_stack.copy()
169        self._call_stack.current_function_address = v
170
171    @property
172    def current_stack_pointer(self):
173        return self._call_stack.current_stack_pointer
174
175    def __repr__(self):
176        if isinstance(self.addr, SootAddressDescriptor):
177            return "<Entry {} {}>".format(self.addr, self.jumpkind)
178        else:
179            return "<Entry %#08x %% %s>" % (self.addr, self.jumpkind)
180