diff --git a/angr/analyses/ddg.py b/angr/analyses/ddg.py index a7626f8b4..14f2f79f7 100644 --- a/angr/analyses/ddg.py +++ b/angr/analyses/ddg.py @@ -1156,9 +1156,11 @@ class DDG(Analysis): if not action.reg_deps and not action.tmp_deps: # moving a constant into the register # try to parse out the constant from statement - const_variable = SimConstantVariable(size=1) if statement is not None and isinstance(statement.data, pyvex.IRExpr.Const): const_variable = SimConstantVariable(value=statement.data.con.value, size=statement.data.con.size) + else: + # use a default value of 0 if we cannot find the constant + const_variable = SimConstantVariable(1, value=0) const_pv = ProgramVariable(const_variable, location, arch=self.project.arch) self._data_graph_add_edge(const_pv, pv) @@ -1229,7 +1231,7 @@ class DDG(Analysis): 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(size=1) + const_variable = SimConstantVariable(size=1, value=0) # default value if we can't find the constant if statement is not None: if isinstance(statement, pyvex.IRStmt.Dirty): l.warning("Dirty statements are not supported in DDG for now.") diff --git a/angr/protos/variables.proto b/angr/protos/variables.proto index 1bde11bd0..4bc0c54ec 100644 --- a/angr/protos/variables.proto +++ b/angr/protos/variables.proto @@ -23,8 +23,9 @@ message TemporaryVariable { message ConstantVariable { VariableBase base = 1; uint32 size = 2; - uint64 value = 3; + uint64 value = 3; // the absolute value optional bytes long_value = 4; // for large constants over 64 bits + bool is_negative = 5; } diff --git a/angr/sim_variable.py b/angr/sim_variable.py index f73bdade3..89fa3234b 100644 --- a/angr/sim_variable.py +++ b/angr/sim_variable.py @@ -116,9 +116,12 @@ class SimConstantVariable(SimVariable): __slots__ = ["value"] - def __init__(self, size: int, ident=None, value=None, region=None): + def __init__(self, size: int, *, value: int | float, region: int | None = None, ident=None): super().__init__(ident=ident, region=region, size=size) - self.value = value + is_negative = value < 0 + abs_value = -value if is_negative else value + abs_value = abs_value & ((1 << (size * 8)) - 1) if isinstance(abs_value, int) else abs_value + self.value = -abs_value if is_negative else abs_value def __repr__(self): return f"<{self.region}|const {self.value}>" @@ -148,7 +151,7 @@ class SimConstantVariable(SimVariable): @property def key(self) -> tuple[str | int | None, ...]: - return ("const", self.value, self.size, self.ident) + return "const", self.value, self.size, self.ident @classmethod def _get_cmsg(cls): @@ -158,17 +161,21 @@ class SimConstantVariable(SimVariable): obj = self._get_cmsg() self._set_base(obj) obj.size = self.size + abs_value = self.value if self.value >= 0 else -self.value if self.bits > 64: assert isinstance(self.value, int) # TODO: Handle float - obj.long_value = int.to_bytes(self.value, byteorder="little") + num_bytes = (self.bits + 7) // 8 + obj.long_value = self.value.to_bytes(num_bytes, byteorder="little") else: - obj.value = self.value + obj.value = abs_value + obj.is_negative = self.value is not None and self.value < 0 return obj @classmethod def parse_from_cmessage(cls, cmsg, **kwargs): value = int.from_bytes(cmsg.long_value, byteorder="little") if cmsg.size > 64 else cmsg.value + value = -value if cmsg.is_negative else value obj = cls(cmsg.size, value=value) obj._from_base(cmsg) return obj diff --git a/tests/serialization/test_serialization.py b/tests/serialization/test_serialization.py index bf3d45812..24afc9274 100755 --- a/tests/serialization/test_serialization.py +++ b/tests/serialization/test_serialization.py @@ -12,7 +12,7 @@ import tempfile import unittest import angr -from angr.sim_variable import SimStackVariable +from angr.sim_variable import SimConstantVariable, SimStackVariable from tests.common import bin_location test_location = os.path.join(bin_location, "tests") @@ -135,6 +135,34 @@ class TestSerialization(unittest.TestCase): cmsg = v1.serialize_to_cmessage() assert cmsg.offset == 0x7FFF_DEAD + def test_simconstantvariable_value_overflow(self): + v0 = SimConstantVariable(16, value=0x8000_0000_0000_0000_0000_0001, ident="c_0") + cmsg = v0.serialize_to_cmessage() + assert cmsg.size == 16 + assert cmsg.long_value == (0x8000_0000_0000_0000_0000_0001).to_bytes(16, byteorder="little") + assert cmsg.is_negative is False + + def test_simconstantvariable_value_out_of_range(self): + v1 = SimConstantVariable(8, value=-0x8000_0000_0000_0000, ident="c_1") + cmsg = v1.serialize_to_cmessage() + assert cmsg.size == 8 + assert cmsg.value == 0x8000_0000_0000_0000 + assert cmsg.is_negative is True + + def test_simconstantvariable_value_too_long(self): + v2 = SimConstantVariable(8, value=0x1_FFFF_FFFF_FFFF_FFF0, ident="c_2") + cmsg = v2.serialize_to_cmessage() + assert cmsg.size == 8 + assert cmsg.value == 0xFFFF_FFFF_FFFF_FFF0 + assert cmsg.is_negative is False + + def test_simconstantvariable_negative_value(self): + v1 = SimConstantVariable(8, value=-1, ident="c_1") + cmsg = v1.serialize_to_cmessage() + assert cmsg.size == 8 + assert cmsg.value == 1 + assert cmsg.is_negative is True + if __name__ == "__main__": unittest.main()