SimConstantVariable: Fix overflows and value out of range errors. (#6738)

* SimConstantVariable: Fix overflows and value out of range errors.

* Fix usages of SimConstantVariable.

* Mask the value.

* Fix negative values.
This commit is contained in:
Fish 2026-07-29 13:58:20 -07:00 committed by GitHub
parent d38cc5a019
commit 61bac8ffd0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 47 additions and 9 deletions

View file

@ -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.")

View file

@ -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;
}

View file

@ -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

View file

@ -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()