Skip to content

Commit

Permalink
Merge pull request #2383 from crytic/fix/guard-implicit-conversion-of…
Browse files Browse the repository at this point in the history
…-literals

fix: guard literal implicit conversion for arrays
  • Loading branch information
0xalpharush authored Mar 29, 2024
2 parents 5e7562a + 6bda75d commit c704a32
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 40 deletions.
45 changes: 18 additions & 27 deletions slither/slithir/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -1926,35 +1926,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 @@ -1964,6 +1950,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 @@ -1973,11 +1960,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 @@ -227,15 +227,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 @@ -270,6 +271,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 @@ -430,7 +432,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

0 comments on commit c704a32

Please sign in to comment.