Skip to content

Commit

Permalink
Add try/catch, add tests, refactor guidance/echidna to reduce pylint …
Browse files Browse the repository at this point in the history
…exceptions
  • Loading branch information
montyly committed Oct 17, 2024
1 parent 607b22a commit f6b2509
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 45 deletions.
110 changes: 65 additions & 45 deletions slither/printers/guidance/echidna.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from slither.core.expressions import NewContract
from slither.core.slither_core import SlitherCore
from slither.core.solidity_types import TypeAlias
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.variable import Variable
from slither.printers.abstract_printer import AbstractPrinter
Expand Down Expand Up @@ -179,29 +180,74 @@ class ConstantValue(NamedTuple): # pylint: disable=inherit-non-class,too-few-pu
type: str


def _extract_constants_from_irs( # pylint: disable=too-many-branches,too-many-nested-blocks
def _extract_constant_from_read(
ir: Operation,
r: SourceMapping,
all_cst_used: List[ConstantValue],
all_cst_used_in_binary: Dict[str, List[ConstantValue]],
context_explored: Set[Node],
) -> None:
var_read = r.points_to_origin if isinstance(r, ReferenceVariable) else r
# Do not report struct_name in a.struct_name
if isinstance(ir, Member):
return
if isinstance(var_read, Variable) and var_read.is_constant:
# In case of type conversion we use the destination type
if isinstance(ir, TypeConversion):
if isinstance(ir.type, TypeAlias):
value_type = ir.type.type
else:
value_type = ir.type
else:
value_type = var_read.type
try:
value = ConstantFolding(var_read.expression, value_type).result()
all_cst_used.append(ConstantValue(str(value), str(value_type)))
except NotConstant:
pass
if isinstance(var_read, Constant):
all_cst_used.append(ConstantValue(str(var_read.value), str(var_read.type)))
if isinstance(var_read, StateVariable):
if var_read.node_initialization:
if var_read.node_initialization.irs:
if var_read.node_initialization in context_explored:
return
context_explored.add(var_read.node_initialization)
_extract_constants_from_irs(
var_read.node_initialization.irs,
all_cst_used,
all_cst_used_in_binary,
context_explored,
)


def _extract_constant_from_binary(
ir: Binary,
all_cst_used: List[ConstantValue],
all_cst_used_in_binary: Dict[str, List[ConstantValue]],
):
for r in ir.read:
if isinstance(r, Constant):
all_cst_used_in_binary[str(ir.type)].append(ConstantValue(str(r.value), str(r.type)))
if isinstance(ir.variable_left, Constant) or isinstance(ir.variable_right, Constant):
if ir.lvalue:
try:
type_ = ir.lvalue.type
cst = ConstantFolding(ir.expression, type_).result()
all_cst_used.append(ConstantValue(str(cst.value), str(type_)))
except NotConstant:
pass


def _extract_constants_from_irs(
irs: List[Operation],
all_cst_used: List[ConstantValue],
all_cst_used_in_binary: Dict[str, List[ConstantValue]],
context_explored: Set[Node],
) -> None:
for ir in irs:
if isinstance(ir, Binary):
for r in ir.read:
if isinstance(r, Constant):
all_cst_used_in_binary[str(ir.type)].append(
ConstantValue(str(r.value), str(r.type))
)
if isinstance(ir.variable_left, Constant) or isinstance(
ir.variable_right, Constant
):
if ir.lvalue:
try:
type_ = ir.lvalue.type
cst = ConstantFolding(ir.expression, type_).result()
all_cst_used.append(ConstantValue(str(cst.value), str(type_)))
except NotConstant:
pass
_extract_constant_from_binary(ir, all_cst_used, all_cst_used_in_binary)
if isinstance(ir, TypeConversion):
if isinstance(ir.variable, Constant):
if isinstance(ir.type, TypeAlias):
Expand All @@ -222,35 +268,9 @@ def _extract_constants_from_irs( # pylint: disable=too-many-branches,too-many-n
except ValueError: # index could fail; should never happen in working solidity code
pass
for r in ir.read:
var_read = r.points_to_origin if isinstance(r, ReferenceVariable) else r
# Do not report struct_name in a.struct_name
if isinstance(ir, Member):
continue
if isinstance(var_read, Variable) and var_read.is_constant:
# In case of type conversion we use the destination type
if isinstance(ir, TypeConversion):
if isinstance(ir.type, TypeAlias):
value_type = ir.type.type
else:
value_type = ir.type
else:
value_type = var_read.type
value = ConstantFolding(var_read.expression, value_type).result()
all_cst_used.append(ConstantValue(str(value), str(value_type)))
if isinstance(var_read, Constant):
all_cst_used.append(ConstantValue(str(var_read.value), str(var_read.type)))
if isinstance(var_read, StateVariable):
if var_read.node_initialization:
if var_read.node_initialization.irs:
if var_read.node_initialization in context_explored:
continue
context_explored.add(var_read.node_initialization)
_extract_constants_from_irs(
var_read.node_initialization.irs,
all_cst_used,
all_cst_used_in_binary,
context_explored,
)
_extract_constant_from_read(
ir, r, all_cst_used, all_cst_used_in_binary, context_explored
)


def _extract_constants(
Expand Down
22 changes: 22 additions & 0 deletions tests/unit/slithir/test_constantfolding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from pathlib import Path

from slither import Slither
from slither.printers.guidance.echidna import _extract_constants, ConstantValue

TEST_DATA_DIR = Path(__file__).resolve().parent / "test_data"


def test_enum_max_min(solc_binary_path) -> None:
solc_path = solc_binary_path("0.8.19")
slither = Slither(Path(TEST_DATA_DIR, "constantfolding.sol").as_posix(), solc=solc_path)

contracts = slither.get_contract_from_name("A")

constants = _extract_constants(contracts)[0]["A"]["use()"]

assert set(constants) == {
ConstantValue(value="2", type="uint256"),
ConstantValue(value="10", type="uint256"),
ConstantValue(value="100", type="uint256"),
ConstantValue(value="4294967295", type="uint32"),
}
19 changes: 19 additions & 0 deletions tests/unit/slithir/test_data/constantfolding.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type MyType is uint256;

contract A{

enum E{
a,b,c
}


uint a = 10;
E b = type(E).max;
uint c = type(uint32).max;
MyType d = MyType.wrap(100);

function use() public returns(uint){
E e = b;
return a +c + MyType.unwrap(d);
}
}

0 comments on commit f6b2509

Please sign in to comment.