Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the use of dirty in DirtyStatement. #250

Merged
merged 3 commits into from
Oct 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading