Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
def test_loadg_no_constraint_creation():
state = SimState(arch='armel', mode='symbolic')
engine = SimEngineVEX()
from angr.engines.vex.statements.loadg import SimIRStmt_LoadG
stmt = pyvex.IRStmt.LoadG('Iend_LE', 'ILGop_16Uto32',
0, # dst
pyvex.IRExpr.Const(pyvex.const.U32(0x2000)), # addr (src)
pyvex.IRExpr.Const(pyvex.const.U32(0x1337)), # alt
pyvex.IRExpr.RdTmp(1) # guard
)
tyenv = pyvex.IRTypeEnv(state.arch)
tyenv.types = [ 'Ity_I32', 'Ity_I32' ]
state.scratch.set_tyenv(tyenv)
state.scratch.temps[1] = state.solver.BVS('tmp_1', 32)
SimIRStmt_LoadG(engine, state, stmt)
# LOADG should not create new constraints - it is a simple conditional memory read. The conditions should only be
# used inside the value AST to guard the memory read.
assert not state.solver.constraints
assert state.scratch.temps[0] is not None
assert state.scratch.temps[0].variables.issuperset(state.scratch.temps[1].variables)
assert state.scratch.temps[0].op == 'If'
def _resolve_expr(expr):
if type(expr) is pyvex.IRExpr.Binop:
arg0, arg1 = expr.args
if expr.op.startswith('Iop_Add'):
return _resolve_expr(arg0) + _resolve_expr(arg1)
elif expr.op.startswith('Iop_Sub'):
return _resolve_expr(arg0) - _resolve_expr(arg1)
elif type(expr) is pyvex.IRExpr.RdTmp and expr.tmp in tmps and tmps[expr.tmp] is not None:
return tmps[expr.tmp]
elif type(expr) is pyvex.IRExpr.Const:
return Constant(expr.con.value)
elif type(expr) is pyvex.IRExpr.Get:
return state.get(expr.offset)
elif self.track_mem and type(expr) is pyvex.IRExpr.Load:
return state.load(_resolve_expr(expr.addr))
raise CouldNotResolveException
instr_addr = instr_addrs[0]
instr_addrs = instr_addrs[1 : ]
next_instr_addr = instr_addrs[0] if instr_addrs else None
elif type(stmt) is pyvex.IRStmt.WrTmp: # pylint: disable=unidiomatic-typecheck
if type(stmt.data) is pyvex.IRExpr.Load: # pylint: disable=unidiomatic-typecheck
# load
# e.g. t7 = LDle:I64(0x0000000000600ff8)
_process(irsb, stmt, stmt_idx, stmt.data.addr, instr_addr, next_instr_addr)
elif type(stmt.data) in (pyvex.IRExpr.Binop, ): # pylint: disable=unidiomatic-typecheck
# binary operation
for arg in stmt.data.args:
_process(irsb, stmt, stmt_idx, arg, instr_addr, next_instr_addr)
elif type(stmt.data) is pyvex.IRExpr.Const: # pylint: disable=unidiomatic-typecheck
_process(irsb, stmt, stmt_idx, stmt.data, instr_addr, next_instr_addr)
elif type(stmt) is pyvex.IRStmt.Put: # pylint: disable=unidiomatic-typecheck
# put
# e.g. PUT(rdi) = 0x0000000000400714
if stmt.offset not in (self._initial_state.arch.ip_offset, ):
_process(irsb, stmt, stmt_idx, stmt.data, instr_addr, next_instr_addr)
elif type(stmt) is pyvex.IRStmt.Store: # pylint: disable=unidiomatic-typecheck
# store addr
_process(irsb, stmt, stmt_idx, stmt.addr, instr_addr, next_instr_addr)
# store data
_process(irsb, stmt, stmt_idx, stmt.data, instr_addr, next_instr_addr)
def _try_resolve_single_constant_loads(load_stmt, cfg, addr):
"""
Resolve cases where only a single constant load is required to resolve the indirect jump. Strictly speaking, it
is not a jump table, but we resolve it here anyway.
:param load_stmt: The pyvex.IRStmt.Load statement that loads an address.
:param cfg: The CFG instance.
:param int addr: Address of the jump table block.
:return: A jump target, or None if it cannot be resolved.
:rtype: int or None
"""
# If we're just reading a constant, don't bother with the rest of this mess!
if isinstance(load_stmt, pyvex.IRStmt.WrTmp):
if type(load_stmt.data.addr) is pyvex.IRExpr.Const:
# It's directly loading from a constant address
# e.g.,
# ldr r0, =main+1
# blx r0
# It's not a jump table, but we resolve it anyway
jump_target_addr = load_stmt.data.addr.con.value
jump_target = cfg._fast_memory_load_pointer(jump_target_addr)
if jump_target is None:
l.info("Constant indirect jump %#x points outside of loaded memory to %#08x", addr,
jump_target_addr)
raise NotAJumpTableNotification()
l.info("Resolved constant indirect jump from %#08x to %#08x", addr, jump_target_addr)
return jump_target
elif isinstance(load_stmt, pyvex.IRStmt.LoadG):
for addr_0, addr_1 in zip(addrs[:-1], addrs[1:]):
if addr_1 in predetermined_function_addrs:
continue
func_0 = functions[addr_0]
if len(func_0.block_addrs) == 1:
block = next(func_0.blocks)
if block.vex.jumpkind not in ('Ijk_Boring', 'Ijk_InvalICache'):
continue
# Skip alignment blocks
if self._is_noop_block(self.project.arch, block):
continue
target = block.vex.next
if isinstance(target, pyvex.IRExpr.Const): # pylint: disable=unidiomatic-typecheck
target_addr = target.con.value
elif type(target) in (pyvex.IRConst.U16, pyvex.IRConst.U32, pyvex.IRConst.U64): # pylint: disable=unidiomatic-typecheck
target_addr = target.value
elif type(target) is int: # pylint: disable=unidiomatic-typecheck
target_addr = target
else:
continue
if target_addr != addr_1:
continue
cfgnode_0 = self.model.get_any_node(addr_0)
cfgnode_1 = self.model.get_any_node(addr_1)
if cfgnode_0 is None or cfgnode_1 is None:
continue
for i, stmt in reversed(list(enumerate(stmts))):
if tmp_exit is None:
# Looking for the Exit statement
if isinstance(stmt, pyvex.IRStmt.Exit) and \
isinstance(stmt.guard, pyvex.IRExpr.RdTmp):
tmp_exit = stmt.guard.tmp
dst = stmt.dst
exit_stmt_idx = i
else:
# Looking for the WrTmp statement
if isinstance(stmt, pyvex.IRStmt.WrTmp) and \
stmt.tmp == tmp_exit:
if isinstance(stmt.data, pyvex.IRExpr.Binop) and \
stmt.data.op == 'Iop_CmpEQ32' and \
isinstance(stmt.data.child_expressions[0], pyvex.IRExpr.Const) and \
isinstance(stmt.data.child_expressions[1], pyvex.IRExpr.Const) and \
stmt.data.child_expressions[0].con.value == stmt.data.child_expressions[1].con.value:
# Create a new IRConst
irconst = pyvex.IRExpr.Const() # XXX: THIS IS BROKEN FIX THIS VERY SOON
irconst.con = dst
irconst.is_atomic = True
irconst.result_type = dst.type
irconst.tag = 'Iex_Const'
block.statements = block.statements[ : exit_stmt_idx] + block.statements[exit_stmt_idx + 1 : ]
# Replace the default exit!
block.next = irconst
else:
break
# Occupy the block
self._seg_list.occupy(addr, irsb.size)
except (SimEngineError, SimMemoryError):
return
# Get all possible successors
next, jumpkind = irsb.next, irsb.jumpkind
successors = [ (i.dst, i.jumpkind) for i in irsb.statements if type(i) is pyvex.IRStmt.Exit]
successors.append((next, jumpkind))
# Process each successor
for suc in successors:
target, jumpkind = suc
if type(target) is pyvex.IRExpr.Const:
next_addr = target.con.value
else:
next_addr = None
if jumpkind == 'Ijk_Boring' and next_addr is not None:
remaining_exits.add((current_function_addr, next_addr,
addr, None))
elif jumpkind == 'Ijk_Call' and next_addr is not None:
# Log it before we cut the tracing :)
if jumpkind == "Ijk_Call":
if current_function_addr != -1:
self.functions.add(current_function_addr)
self.functions.add(next_addr)
self.call_map.add_edge(current_function_addr, next_addr)
else:
isinstance(stmt.guard, pyvex.IRExpr.RdTmp):
tmp_exit = stmt.guard.tmp
dst = stmt.dst
exit_stmt_idx = i
else:
# Looking for the WrTmp statement
if isinstance(stmt, pyvex.IRStmt.WrTmp) and \
stmt.tmp == tmp_exit:
if isinstance(stmt.data, pyvex.IRExpr.Binop) and \
stmt.data.op == 'Iop_CmpEQ32' and \
isinstance(stmt.data.child_expressions[0], pyvex.IRExpr.Const) and \
isinstance(stmt.data.child_expressions[1], pyvex.IRExpr.Const) and \
stmt.data.child_expressions[0].con.value == stmt.data.child_expressions[1].con.value:
# Create a new IRConst
irconst = pyvex.IRExpr.Const() # XXX: THIS IS BROKEN FIX THIS VERY SOON
irconst.con = dst
irconst.is_atomic = True
irconst.result_type = dst.type
irconst.tag = 'Iex_Const'
block.statements = block.statements[ : exit_stmt_idx] + block.statements[exit_stmt_idx + 1 : ]
# Replace the default exit!
block.next = irconst
else:
break
return block
# assignment: dst_tmp = src_reg
for s in filter(lambda x: isinstance(x.variable, SimRegisterVariable), self._variables_per_statement):
self._ast_graph.add_edge(s, pv)
elif isinstance(statement.data, pyvex.IRExpr.Load):
# assignment: dst_tmp = [ src_mem ]
for s in filter(lambda x: isinstance(x.variable, SimMemoryVariable), self._variables_per_statement):
self._ast_graph.add_edge(s, pv)
if not action.tmp_deps and not self._variables_per_statement and not ast:
# read in a constant
# try to parse out the constant from statement
const_variable = SimConstantVariable()
if statement is not None:
if isinstance(statement, pyvex.IRStmt.Dirty):
l.warning('Dirty statements are not supported in DDG for now.')
elif isinstance(statement.data, pyvex.IRExpr.Const):
const_variable = SimConstantVariable(value=statement.data.con.value)
const_pv = ProgramVariable(const_variable, location, arch=self.project.arch)
self._data_graph_add_edge(const_pv, pv)
tmp_exit = stmt.guard.tmp
dst = stmt.dst
exit_stmt_idx = i
else:
# Looking for the WrTmp statement
if isinstance(stmt, pyvex.IRStmt.WrTmp) and \
stmt.tmp == tmp_exit:
if isinstance(stmt.data, pyvex.IRExpr.Binop) and \
stmt.data.op == 'Iop_CmpEQ32' and \
isinstance(stmt.data.child_expressions[0], pyvex.IRExpr.Const) and \
isinstance(stmt.data.child_expressions[1], pyvex.IRExpr.Const) and \
stmt.data.child_expressions[0].con.value == stmt.data.child_expressions[
1].con.value:
# Create a new IRConst
irconst = pyvex.IRExpr.Const.__new__() # XXX: does this work???
irconst.con = dst
irconst.is_atomic = True
irconst.result_type = dst.type
irconst.tag = 'Iex_Const'
block.statements = block.statements[: exit_stmt_idx] + block.statements[exit_stmt_idx + 1:]
# Replace the default exit!
block.next = irconst
else:
break
return block