Preserve indirect store addresses across type casts

This commit is contained in:
Yan 2026-08-15 13:01:04 +00:00
parent 503b1be066
commit 83d7c034ba
2 changed files with 64 additions and 8 deletions

View file

@ -3328,15 +3328,18 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis, Serializab
lvalue: bool,
renegotiate_type: Callable[[SimType, SimType], SimType] = lambda old, proposed: old,
) -> CExpression:
def _force_type_cast(src_type_: SimType, dst_type_: SimType, expr_: CExpression) -> CUnaryOp:
def _force_type_cast(
src_type_: SimType, dst_type_: SimType, expr_: CExpression, take_reference: bool
) -> CUnaryOp:
src_type_ptr = SimTypePointer(src_type_).with_arch(self.project.arch)
dst_type_ptr = SimTypePointer(dst_type_).with_arch(self.project.arch)
cast_expr = CUnaryOp("Reference", expr_, codegen=self) if take_reference else expr_
return CUnaryOp(
"Dereference",
CTypeCast(
src_type_ptr,
dst_type_ptr,
CUnaryOp("Reference", expr_, codegen=self),
cast_expr,
codegen=self,
),
codegen=self,
@ -3369,11 +3372,11 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis, Serializab
# case 2: we're done because we can never find it and we might as well stop early
if base_expr:
if not type_equals(base_type, data_type):
return _force_type_cast(base_type, data_type, base_expr)
return _force_type_cast(base_type, data_type, base_expr, True)
return base_expr
if not type_equals(base_type, data_type):
return _force_type_cast(base_type, data_type, expr)
return _force_type_cast(base_type, data_type, expr, False)
return CUnaryOp("Dereference", expr, codegen=self)
stride = 1 if base_type.size is None else base_type.size // self.project.arch.byte_width or 1

View file

@ -7,7 +7,20 @@ __package__ = __package__ or "tests.analyses.decompiler" # pylint:disable=redef
import unittest
import angr
from angr.ailment import Expr
from angr.ailment import Expr, Manager, Stmt
from angr.ailment.expression import VirtualVariableCategory
from angr.analyses.decompiler import CStructuredCodeGenerator
from angr.analyses.decompiler.variable_map import VariableMap
from angr.sim_type import SimTypeChar, SimTypePointer
from angr.sim_variable import SimRegisterVariable, SimStackVariable
def _make_codegen() -> CStructuredCodeGenerator:
proj = angr.load_shellcode(b"\x31\xc0\xc3", arch="AMD64")
cfg = proj.analyses.CFGFast(normalize=True)
codegen = proj.analyses.Decompiler(cfg.functions[0], cfg=cfg).codegen
assert isinstance(codegen, CStructuredCodeGenerator)
return codegen
class TestConvertRendering(unittest.TestCase):
@ -16,9 +29,7 @@ class TestConvertRendering(unittest.TestCase):
@classmethod
def setUpClass(cls):
# any decompilation will do; all we need is a codegen instance to render expressions with
proj = angr.load_shellcode(b"\x31\xc0\xc3", arch="AMD64") # xor eax, eax; ret
cfg = proj.analyses.CFGFast(normalize=True)
cls.codegen = proj.analyses.Decompiler(cfg.functions[0], cfg=cfg).codegen
cls.codegen = _make_codegen()
def _render(self, from_bits: int, to_bits: int, value: int = 0x1234) -> str:
conv = Expr.Convert(0, from_bits, to_bits, False, Expr.Const(0, value, from_bits))
@ -43,5 +54,47 @@ class TestConvertRendering(unittest.TestCase):
assert self._render(32, 64, value=3) == "(unsigned long long)3"
class TestStoreRendering(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.codegen = _make_codegen()
def test_mismatched_store_cast_distinguishes_pointer_from_storage(self):
manager = Manager(arch=self.codegen.project.arch)
addr = Expr.VirtualVariable(
manager.next_atom(), 1, self.codegen.project.arch.bits, VirtualVariableCategory.REGISTER
)
data = Expr.Const(manager.next_atom(), 0x11223344, 32)
pointer_store = Stmt.Store(
manager.next_atom(), addr, data, 4, self.codegen.project.arch.memory_endness, ins_addr=0x401000
)
direct_store = Stmt.Store(
manager.next_atom(), addr, data, 4, self.codegen.project.arch.memory_endness, ins_addr=0x401004
)
addr_variable = SimRegisterVariable(0x28, self.codegen.project.arch.bytes, ident="ir_test", name="iter")
storage_variable = SimStackVariable(-0x10, 4, ident="is_test", name="storage")
variable_map = VariableMap()
variable_map.set_variable(addr, addr_variable)
variable_map.set_variable(direct_store, storage_variable)
variable_manager = self.codegen.kb.dec_variables[self.codegen._func.addr]
variable_manager.set_unified_variable(addr_variable, addr_variable)
variable_manager.set_unified_variable(storage_variable, storage_variable)
variable_manager.set_variable_type(
addr_variable, SimTypePointer(SimTypeChar()).with_arch(self.codegen.project.arch)
)
variable_manager.set_variable_type(storage_variable, SimTypeChar().with_arch(self.codegen.project.arch))
old_variable_map = self.codegen._variable_map
self.codegen._variable_map = variable_map
try:
pointer_rendered = self.codegen._handle(pointer_store, is_expr=False).c_repr()
direct_rendered = self.codegen._handle(direct_store, is_expr=False).c_repr()
finally:
self.codegen._variable_map = old_variable_map
assert direct_rendered == "*((unsigned int *)&storage) = 287454020;\n"
assert pointer_rendered == "*((unsigned int *)iter) = 287454020;\n"
if __name__ == "__main__":
unittest.main()