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