From f04a3c15f8099dbf6d40f6093e0316ce191fa1a8 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 28 Mar 2024 22:14:13 -0500 Subject: [PATCH 1/2] fix: guard literal implicit conversion for arrays --- slither/slithir/convert.py | 44 ++++++--------- slither/slithir/operations/init_array.py | 10 ++-- slither/slithir/operations/new_array.py | 4 +- .../visitors/slithir/expression_to_slithir.py | 10 ++-- ..._parsing__vyper_cfgir_for2_for_loop__0.txt | 2 +- tests/unit/slithir/test_ssa_generation.py | 54 +++++++++++++++++++ 6 files changed, 84 insertions(+), 40 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index e2b1fe7ba6..a4706650af 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -1922,35 +1922,18 @@ def convert_constant_types(irs: List[Operation]) -> None: while was_changed: was_changed = False for ir in irs: - if isinstance(ir, Assignment): - if isinstance(ir.lvalue.type, ElementaryType): - if ir.lvalue.type.type in ElementaryTypeInt: - if isinstance(ir.rvalue, Function): - continue - if isinstance(ir.rvalue, TupleVariable): - # TODO: fix missing Unpack conversion - continue - if isinstance(ir.rvalue.type, TypeAlias): - ir.rvalue.set_type(ElementaryType(ir.lvalue.type.name)) - was_changed = True - elif ir.rvalue.type.type not in ElementaryTypeInt: - ir.rvalue.set_type(ElementaryType(ir.lvalue.type.type)) + if isinstance(ir, (Assignment, Binary)): + if isinstance(ir.lvalue.type, ElementaryType) and ir.lvalue.type.type in ElementaryTypeInt: + for r in ir.read: + if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt: + r.set_type(ElementaryType(ir.lvalue.type.type)) was_changed = True - if isinstance(ir, Binary): - if isinstance(ir.lvalue.type, ElementaryType): - if ir.lvalue.type.type in ElementaryTypeInt: - for r in ir.read: - if r.type.type not in ElementaryTypeInt: - r.set_type(ElementaryType(ir.lvalue.type.type)) - was_changed = True + if isinstance(ir, (HighLevelCall, InternalCall)): func = ir.function if isinstance(func, StateVariable): types = export_nested_types_from_variable(func) else: - if func is None: - # TODO: add POP instruction - break types = [p.type for p in func.parameters] assert len(types) == len(ir.arguments) for idx, arg in enumerate(ir.arguments): @@ -1960,6 +1943,7 @@ def convert_constant_types(irs: List[Operation]) -> None: if arg.type.type not in ElementaryTypeInt: arg.set_type(ElementaryType(t.type)) was_changed = True + if isinstance(ir, NewStructure): st = ir.structure for idx, arg in enumerate(ir.arguments): @@ -1969,14 +1953,18 @@ def convert_constant_types(irs: List[Operation]) -> None: if arg.type.type not in ElementaryTypeInt: arg.set_type(ElementaryType(e.type.type)) was_changed = True + + def is_elementary_array(t): + return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType) + if isinstance(ir, InitArray): - if isinstance(ir.lvalue.type, ArrayType): - if isinstance(ir.lvalue.type.type, ElementaryType): + if is_elementary_array(ir.lvalue.type): if ir.lvalue.type.type.type in ElementaryTypeInt: for r in ir.read: - if r.type.type.type not in ElementaryTypeInt: - r.set_type(ElementaryType(ir.lvalue.type.type.type)) - was_changed = True + if isinstance(r, Constant) and is_elementary_array(r.type): + if r.type.type.type not in ElementaryTypeInt: + r.set_type(ElementaryType(ir.lvalue.type.type.type)) + was_changed = True # endregion diff --git a/slither/slithir/operations/init_array.py b/slither/slithir/operations/init_array.py index ec2a63ef00..82adf595ca 100644 --- a/slither/slithir/operations/init_array.py +++ b/slither/slithir/operations/init_array.py @@ -1,14 +1,14 @@ from typing import List, Union from slither.slithir.operations.lvalue import OperationWithLValue -from slither.slithir.utils.utils import is_valid_rvalue -from slither.slithir.variables.constant import Constant +from slither.slithir.utils.utils import is_valid_rvalue, RVALUE from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA +from slither.slithir.utils.utils import RVALUE class InitArray(OperationWithLValue): def __init__( - self, init_values: List[Constant], lvalue: Union[TemporaryVariableSSA, TemporaryVariable] + self, init_values: List[RVALUE], lvalue: Union[TemporaryVariableSSA, TemporaryVariable] ) -> None: # init_values can be an array of n dimension # reduce was removed in py3 @@ -30,11 +30,11 @@ def check(elem): self._lvalue = lvalue @property - def read(self) -> List[Constant]: + def read(self) -> List[RVALUE]: return self._unroll(self.init_values) @property - def init_values(self) -> List[Constant]: + def init_values(self) -> List[RVALUE]: return list(self._init_values) def __str__(self): diff --git a/slither/slithir/operations/new_array.py b/slither/slithir/operations/new_array.py index 917bb11ee9..e4f508efcc 100644 --- a/slither/slithir/operations/new_array.py +++ b/slither/slithir/operations/new_array.py @@ -3,9 +3,9 @@ from slither.core.solidity_types.array_type import ArrayType from slither.slithir.operations.call import Call from slither.slithir.operations.lvalue import OperationWithLValue +from slither.slithir.utils.utils import RVALUE if TYPE_CHECKING: - from slither.slithir.variables.constant import Constant from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA @@ -27,7 +27,7 @@ def array_type(self) -> "ArrayType": return self._array_type @property - def read(self) -> List["Constant"]: + def read(self) -> List[RVALUE]: return self._unroll(self.arguments) def __str__(self): diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 931dc82ae8..6600dd22bd 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -233,15 +233,16 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None: self._result.append(operation) set_val(expression, None) else: - # Init of array, like - # uint8[2] var = [1,2]; + # For `InitArray`, the rhs is a list or singleton of `TupleExpression` elements. + # Init of array e.g. uint8[2] var = [1,2]; if isinstance(right, list): operation = InitArray(right, left) operation.set_expression(expression) self._result.append(operation) set_val(expression, left) - elif isinstance(left.type, ArrayType): - # Special case for init of array, when the right has only one element + + # Special case for init of array, when the right has only one element e.g. arr = [1]; + elif isinstance(left.type, ArrayType) and not isinstance(right.type, ArrayType): operation = InitArray([right], left) operation.set_expression(expression) self._result.append(operation) @@ -276,6 +277,7 @@ def _post_assignement_operation(self, expression: AssignmentOperation) -> None: self._result.append(operation) else: + operation = convert_assignment( left, right, expression.type, expression.expression_return_type ) diff --git a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt index 9e35f147e3..cf57a06c0b 100644 --- a/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt +++ b/tests/e2e/vyper_parsing/snapshots/ast_parsing__vyper_cfgir_for2_for_loop__0.txt @@ -8,7 +8,7 @@ EXPRESSION: _strategies = strategies IRs: -_strategies(address[3]) = ['strategies(address[3])']"]; +_strategies(address[3]) := strategies(address[3])"]; 1->2; 2[label="Node Type: BEGIN_LOOP 2 "]; diff --git a/tests/unit/slithir/test_ssa_generation.py b/tests/unit/slithir/test_ssa_generation.py index 1ecf82a2dd..5afbc40fc6 100644 --- a/tests/unit/slithir/test_ssa_generation.py +++ b/tests/unit/slithir/test_ssa_generation.py @@ -26,6 +26,7 @@ InternalCall, Index, InitArray, + NewArray, ) from slither.slithir.utils.ssa import is_used_later from slither.slithir.variables import ( @@ -1131,8 +1132,61 @@ def test_issue_2016(slither_from_solidity_source): f = c.functions[0] operations = f.slithir_operations new_op = operations[0] + assert isinstance(new_op, NewArray) lvalue = new_op.lvalue lvalue_type = lvalue.type assert isinstance(lvalue_type, ArrayType) assert lvalue_type.type == ElementaryType("int256") assert lvalue_type.is_dynamic + +def test_issue_2210(slither_from_solidity_source): + source = """ + contract C { + function f (int x) public returns(int) { + int h = 1; + int k = 5; + int[5] memory arr = [x, C.x, C.y, h - k, h + k]; + } + int x= 4; + int y = 5; + } + """ + with slither_from_solidity_source(source) as slither: + c = slither.get_contract_from_name("C")[0] + f = c.functions[0] + operations = f.slithir_operations + new_op = operations[6] + assert isinstance(new_op, InitArray) + lvalue = new_op.lvalue + lvalue_type = lvalue.type + assert isinstance(lvalue_type, ArrayType) + assert lvalue_type.type == ElementaryType("int256") + assert not lvalue_type.is_dynamic + + source2 = """ + contract X { + function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { + /// @solidity memory-safe-assembly + assembly { + casted := a + } + } + } + """ + with slither_from_solidity_source(source2) as slither: + x = slither.get_contract_from_name("X")[0] + f2 = x.functions[0] + operations = f2.slithir_operations + new_op2 = operations[0] + assert isinstance(new_op2, Assignment) + + lvalue = new_op2.lvalue + lvalue_type = lvalue.type + assert isinstance(lvalue_type, ArrayType) + assert lvalue_type.type == ElementaryType("int256") + assert lvalue_type.is_dynamic + + rvalue_type = new_op2.rvalue.type + assert isinstance(rvalue_type, ArrayType) + assert rvalue_type.type == ElementaryType("uint256") + assert rvalue_type.is_dynamic \ No newline at end of file From 53c769d4c71a6472029934e5273dac69674ead51 Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 28 Mar 2024 22:32:27 -0500 Subject: [PATCH 2/2] fmt and lint --- slither/slithir/convert.py | 21 +++++++++++-------- slither/slithir/operations/init_array.py | 1 - .../visitors/slithir/expression_to_slithir.py | 2 +- tests/unit/slithir/test_ssa_generation.py | 7 ++++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index a4706650af..f626f9e008 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -1923,7 +1923,10 @@ def convert_constant_types(irs: List[Operation]) -> None: was_changed = False for ir in irs: if isinstance(ir, (Assignment, Binary)): - if isinstance(ir.lvalue.type, ElementaryType) and ir.lvalue.type.type in ElementaryTypeInt: + if ( + isinstance(ir.lvalue.type, ElementaryType) + and ir.lvalue.type.type in ElementaryTypeInt + ): for r in ir.read: if isinstance(r, Constant) and r.type.type not in ElementaryTypeInt: r.set_type(ElementaryType(ir.lvalue.type.type)) @@ -1943,7 +1946,7 @@ def convert_constant_types(irs: List[Operation]) -> None: if arg.type.type not in ElementaryTypeInt: arg.set_type(ElementaryType(t.type)) was_changed = True - + if isinstance(ir, NewStructure): st = ir.structure for idx, arg in enumerate(ir.arguments): @@ -1956,15 +1959,15 @@ def convert_constant_types(irs: List[Operation]) -> None: def is_elementary_array(t): return isinstance(t, ArrayType) and isinstance(t.type, ElementaryType) - + if isinstance(ir, InitArray): if is_elementary_array(ir.lvalue.type): - if ir.lvalue.type.type.type in ElementaryTypeInt: - for r in ir.read: - if isinstance(r, Constant) and is_elementary_array(r.type): - if r.type.type.type not in ElementaryTypeInt: - r.set_type(ElementaryType(ir.lvalue.type.type.type)) - was_changed = True + if ir.lvalue.type.type.type in ElementaryTypeInt: + for r in ir.read: + if isinstance(r, Constant) and is_elementary_array(r.type): + if r.type.type.type not in ElementaryTypeInt: + r.set_type(ElementaryType(ir.lvalue.type.type.type)) + was_changed = True # endregion diff --git a/slither/slithir/operations/init_array.py b/slither/slithir/operations/init_array.py index 82adf595ca..99065d48ad 100644 --- a/slither/slithir/operations/init_array.py +++ b/slither/slithir/operations/init_array.py @@ -3,7 +3,6 @@ from slither.slithir.utils.utils import is_valid_rvalue, RVALUE from slither.slithir.variables.temporary import TemporaryVariable from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA -from slither.slithir.utils.utils import RVALUE class InitArray(OperationWithLValue): diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index 6600dd22bd..e87b3083da 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -438,7 +438,7 @@ def _post_call_expression(self, expression: CallExpression) -> None: set_val(expression, val) def _post_conditional_expression(self, expression: ConditionalExpression) -> None: - raise Exception(f"Ternary operator are not convertible to SlithIR {expression}") + raise SlithIRError(f"Ternary operator are not convertible to SlithIR {expression}") def _post_elementary_type_name_expression( self, diff --git a/tests/unit/slithir/test_ssa_generation.py b/tests/unit/slithir/test_ssa_generation.py index 5afbc40fc6..1a709c2f78 100644 --- a/tests/unit/slithir/test_ssa_generation.py +++ b/tests/unit/slithir/test_ssa_generation.py @@ -1139,6 +1139,7 @@ def test_issue_2016(slither_from_solidity_source): assert lvalue_type.type == ElementaryType("int256") assert lvalue_type.is_dynamic + def test_issue_2210(slither_from_solidity_source): source = """ contract C { @@ -1162,7 +1163,7 @@ def test_issue_2210(slither_from_solidity_source): assert isinstance(lvalue_type, ArrayType) assert lvalue_type.type == ElementaryType("int256") assert not lvalue_type.is_dynamic - + source2 = """ contract X { function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) { @@ -1185,8 +1186,8 @@ def test_issue_2210(slither_from_solidity_source): assert isinstance(lvalue_type, ArrayType) assert lvalue_type.type == ElementaryType("int256") assert lvalue_type.is_dynamic - + rvalue_type = new_op2.rvalue.type assert isinstance(rvalue_type, ArrayType) assert rvalue_type.type == ElementaryType("uint256") - assert rvalue_type.is_dynamic \ No newline at end of file + assert rvalue_type.is_dynamic