Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: guard literal implicit conversion for arrays #2383

Merged
merged 3 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 18 additions & 27 deletions slither/slithir/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -1921,35 +1921,21 @@ 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):
Expand All @@ -1959,6 +1945,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):
Expand All @@ -1968,11 +1955,15 @@ 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 ir.lvalue.type.type.type in ElementaryTypeInt:
for r in ir.read:
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
Expand Down
9 changes: 4 additions & 5 deletions slither/slithir/operations/init_array.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
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


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
Expand All @@ -30,11 +29,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):
Expand Down
4 changes: 2 additions & 2 deletions slither/slithir/operations/new_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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):
Expand Down
12 changes: 7 additions & 5 deletions slither/visitors/slithir/expression_to_slithir.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
)
Expand Down Expand Up @@ -436,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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
"];
Expand Down
55 changes: 55 additions & 0 deletions tests/unit/slithir/test_ssa_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
InternalCall,
Index,
InitArray,
NewArray,
)
from slither.slithir.utils.ssa import is_used_later
from slither.slithir.variables import (
Expand Down Expand Up @@ -1131,8 +1132,62 @@ 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
Loading