Skip to content

Commit

Permalink
Fix the use of dirty in DirtyStatement. (#250)
Browse files Browse the repository at this point in the history
* Fix the use of dirty in DirtyStatement.

* bug fixes
  • Loading branch information
ltfish authored Oct 19, 2024
1 parent 08002f1 commit a3948f1
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 40 deletions.
33 changes: 22 additions & 11 deletions ailment/block_walker.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from collections.abc import Callable

from . import Block
from .statement import Call, Statement, ConditionalJump, Assignment, Store, Return, Jump
from .statement import Call, Statement, ConditionalJump, Assignment, Store, Return, Jump, DirtyStatement
from .expression import (
Load,
Expression,
Expand Down Expand Up @@ -35,6 +35,7 @@ def __init__(self, stmt_handlers=None, expr_handlers=None):
ConditionalJump: self._handle_ConditionalJump,
Jump: self._handle_Jump,
Return: self._handle_Return,
DirtyStatement: self._handle_DirtyStatement,
}

_default_expr_handlers = {
Expand Down Expand Up @@ -129,6 +130,9 @@ def _handle_Return(self, stmt_idx: int, stmt: Return, block: Block | None):
for i, ret_expr in enumerate(stmt.ret_exprs):
self._handle_expr(i, ret_expr, stmt_idx, stmt, block)

def _handle_DirtyStatement(self, stmt_idx: int, stmt: DirtyStatement, block: Block | None):
self._handle_expr(0, stmt.dirty, stmt_idx, stmt, block)

def _handle_Load(self, expr_idx: int, expr: Load, stmt_idx: int, stmt: Statement, block: Block | None):
self._handle_expr(0, expr.addr, stmt_idx, stmt, block)

Expand Down Expand Up @@ -181,12 +185,10 @@ def _handle_MultiStatementExpression(
def _handle_DirtyExpression(
self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
):
for idx, operand in expr.operands:
for idx, operand in enumerate(expr.operands):
self._handle_expr(idx, operand, stmt_idx, stmt, block)
if expr.guard is not None:
self._handle_expr(len(expr.operands) + 1, expr.guard, stmt_idx, stmt, block)
if expr.result_expr is not None:
self._handle_expr(len(expr.operands) + 2, expr.result_expr, stmt_idx, stmt, block)

def _handle_VEXCCallExpression(
self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
Expand Down Expand Up @@ -443,6 +445,22 @@ def _handle_Return(self, stmt_idx: int, stmt: Return, block: Block | None):
return new_stmt
return None

def _handle_DirtyStatement(self, stmt_idx: int, stmt: DirtyStatement, block: Block | None):
changed = False

dirty = self._handle_expr(0, stmt.dirty, stmt_idx, stmt, block)
if dirty is not None and dirty is not stmt.dirty:
changed = True
else:
dirty = stmt.dirty

if changed:
new_stmt = DirtyStatement(stmt.idx, dirty, **stmt.tags)
if self._update_block and block is not None:
block.statements[stmt_idx] = new_stmt
return new_stmt
return None

#
# Expression handlers
#
Expand Down Expand Up @@ -585,12 +603,6 @@ def _handle_DirtyExpression(
else:
new_operands.append(operand)

new_result_expr = expr.result_expr
if expr.result_expr is not None:
new_result_expr = self._handle_expr(1, expr.result_expr, stmt_idx, stmt, block)
if new_result_expr is not None and new_result_expr is not expr.result_expr:
changed = True

new_guard = expr.guard
if expr.guard is not None:
new_guard = self._handle_expr(2, expr.guard, stmt_idx, stmt, block)
Expand All @@ -603,7 +615,6 @@ def _handle_DirtyExpression(
expr.callee,
new_operands,
guard=new_guard,
result_expr=new_result_expr,
mfx=expr.mfx,
maddr=expr.maddr,
msize=expr.msize,
Expand Down
2 changes: 1 addition & 1 deletion ailment/converter_pcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def _convert_binary(self) -> None:

if op is None:
log.warning("p-code: Unsupported opcode of type %s.", opcode.__name__)
out = DirtyExpression(self._manager.next_atom(), opcode.__name__, bits=self._current_op.output.size * 8)
out = DirtyExpression(self._manager.next_atom(), opcode.__name__, [], bits=self._current_op.output.size * 8)
else:
out = BinaryOp(self._manager.next_atom(), op, [in1, in2], signed, ins_addr=self._manager.ins_addr)

Expand Down
35 changes: 26 additions & 9 deletions ailment/converter_vex.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,12 @@ def convert(expr, manager): # pylint:disable=arguments-differ
)

log.warning("VEXExprConverter: Unsupported VEX expression of type %s.", type(expr))
return DirtyExpression(
manager.next_atom(), f"unsupported_{str(type(expr))}", [], bits=expr.result_size(manager.tyenv)
)
try:
bits = expr.result_size(manager.tyenv)
except ValueError:
# e.g., "ValueError: Type Ity_INVALID does not have size"
bits = 0
return DirtyExpression(manager.next_atom(), f"unsupported_{str(type(expr))}", [], bits=bits)

@staticmethod
def convert_list(exprs, manager):
Expand Down Expand Up @@ -424,7 +427,8 @@ def convert(idx, stmt, manager): # pylint:disable=arguments-differ
try:
func = STATEMENT_MAPPINGS[type(stmt)]
except KeyError:
return DirtyStatement(idx, stmt, ins_addr=manager.ins_addr)
dirty = DirtyExpression(manager.next_atom(), str(stmt), [], bits=0)
return DirtyStatement(idx, dirty, ins_addr=manager.ins_addr)

return func(idx, stmt, manager)

Expand Down Expand Up @@ -650,19 +654,31 @@ def CAS(idx, stmt: pyvex.IRStmt.CAS, manager):
def Dirty(idx, stmt: pyvex.IRStmt.Dirty, manager):
# we translate it into tmp = DirtyExpression() if possible

operands = [VEXExprConverter.convert(op, manager) for op in stmt.args]
guard = VEXExprConverter.convert(stmt.guard, manager) if stmt.guard is not None else None
bits = manager.tyenv.sizeof(stmt.tmp) if stmt.tmp != 0xFFFFFFFF else 0
maddr = VEXExprConverter.convert(stmt.mAddr, manager) if stmt.mAddr is not None else None
dirty_expr = DirtyExpression(
manager.next_atom(),
stmt.cee.name,
operands,
guard=guard,
mfx=stmt.mFx,
maddr=maddr,
msize=stmt.mSize,
bits=bits,
)

if stmt.tmp == 0xFFFFFFFF:
return DirtyStatement(
idx,
stmt,
dirty_expr,
ins_addr=manager.ins_addr,
vex_block_addr=manager.block_addr,
vex_stmt_idx=manager.vex_stmt_idx,
)

bits = manager.tyenv.sizeof(stmt.tmp)
tmp = VEXExprConverter.tmp(stmt.tmp, bits, manager)
dirty_expr = DirtyExpression(manager.next_atom(), stmt, bits=bits)

return Assignment(
idx,
tmp,
Expand Down Expand Up @@ -762,7 +778,8 @@ def convert(irsb, manager): # pylint:disable=arguments-differ
if irsb.jumpkind == "Ijk_Call":
target = VEXExprConverter.convert(irsb.next, manager)
elif irsb.jumpkind.startswith("Ijk_Sys"):
target = DirtyExpression(manager.next_atom(), "syscall", manager.arch.bits)
# FIXME: This is a hack to make syscall work. We should have a better way to handle syscalls.
target = DirtyExpression(manager.next_atom(), "syscall", [], bits=manager.arch.bits)
else:
raise NotImplementedError("Unsupported jumpkind")

Expand Down
22 changes: 11 additions & 11 deletions ailment/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,6 @@ class DirtyExpression(Expression):
"callee",
"guard",
"operands",
"result_expr",
"mfx",
"maddr",
"msize",
Expand All @@ -1325,10 +1324,9 @@ def __init__(
operands: list[Expression],
*,
guard: Expression | None = None,
result_expr: Expression | None = None,
mfx: str | None = None,
maddr: Expression | None = None,
msize: Expression | None = None,
msize: int | None = None,
# TODO: fxstate (guest state effects) is not modeled yet
bits=None,
**kwargs,
Expand All @@ -1338,23 +1336,29 @@ def __init__(
self.callee = callee
self.guard = guard
self.operands = operands
self.result_expr = result_expr
self.mfx = mfx
self.maddr = maddr
self.msize = msize
self.bits = bits

@property
def op(self) -> str:
return self.callee

@property
def verbose_op(self) -> str:
return self.op

def likes(self, other):
return (
type(other) is DirtyExpression
and other.callee == self.callee
and is_none_or_likeable(other.guard, self.guard)
and len(self.operands) == len(other.operands)
and all(op1.likes(op2) for op1, op2 in zip(self.operands, other.operands))
and is_none_or_likeable(other.result_expr, self.result_expr)
and other.mfx == self.mfx
and is_none_or_likeable(other.maddr, self.maddr)
and is_none_or_likeable(other.msize, self.msize)
and other.msize == self.msize
and self.bits == other.bits
)

Expand All @@ -1365,10 +1369,9 @@ def matches(self, other):
and is_none_or_matchable(other.guard, self.guard)
and len(self.operands) == len(other.operands)
and all(op1.matches(op2) for op1, op2 in zip(self.operands, other.operands))
and is_none_or_matchable(other.result_expr, self.result_expr)
and other.mfx == self.mfx
and is_none_or_matchable(other.maddr, self.maddr)
and is_none_or_matchable(other.msize, self.msize)
and other.msize == self.msize
and self.bits == other.bits
)

Expand All @@ -1381,7 +1384,6 @@ def _hash_core(self):
self.callee,
self.guard,
tuple(self.operands),
self.result_expr,
self.mfx,
self.maddr,
self.msize,
Expand All @@ -1401,7 +1403,6 @@ def copy(self) -> DirtyExpression:
self.callee,
self.operands,
guard=self.guard,
result_expr=self.result_expr,
mfx=self.mfx,
maddr=self.maddr,
msize=self.msize,
Expand Down Expand Up @@ -1430,7 +1431,6 @@ def replace(self, old_expr: Expression, new_expr: Expression):
self.callee,
new_operands,
guard=self.guard,
result_expr=self.result_expr,
mfx=self.mfx,
maddr=self.maddr,
msize=self.msize,
Expand Down
24 changes: 16 additions & 8 deletions ailment/statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from .utils import stable_hash, is_none_or_likeable, is_none_or_matchable
from .tagged_object import TaggedObject
from .expression import Expression
from .expression import Expression, DirtyExpression

if TYPE_CHECKING:
from angr.calling_conventions import SimCC
Expand Down Expand Up @@ -690,23 +690,31 @@ class DirtyStatement(Statement):
Wrapper around the original statement, which is usually not convertible (temporarily).
"""

__slots__ = ("dirty_stmt",)
__slots__ = ("dirty",)

def __init__(self, idx, dirty_stmt, **kwargs):
def __init__(self, idx, dirty: DirtyExpression, **kwargs):
super().__init__(idx, **kwargs)
self.dirty_stmt = dirty_stmt
self.dirty = dirty

def _hash_core(self):
return stable_hash((DirtyStatement, self.dirty_stmt))
return stable_hash((DirtyStatement, self.dirty))

def __repr__(self):
return "DirtyStatement (%s)" % (type(self.dirty_stmt))
return repr(self.dirty)

def __str__(self):
return "[D] %s" % (str(self.dirty_stmt))
return str(self.dirty)

def replace(self, old_expr, new_expr):
if self.dirty == old_expr:
return True, DirtyStatement(self.idx, new_expr, **self.tags)
r, new_dirty = self.dirty.replace(old_expr, new_expr)
if r:
return True, DirtyStatement(self.idx, new_dirty, **self.tags)
return False, self

def copy(self) -> "DirtyStatement":
return DirtyStatement(self.idx, self.dirty_stmt, **self.tags)
return DirtyStatement(self.idx, self.dirty, **self.tags)


class Label(Statement):
Expand Down

0 comments on commit a3948f1

Please sign in to comment.