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

Use full_name for solidity signature in case of internal function #1356

Merged
merged 2 commits into from
Aug 20, 2022
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
5 changes: 5 additions & 0 deletions slither/core/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -959,6 +959,11 @@ def solidity_signature(self) -> str:
"""
Return a signature following the Solidity Standard
Contract and converted into address

It might still keep internal types (ex: structure name) for internal functions.
The reason is that internal functions allows recursive structure definition, which
can't be converted following the Solidity stand ard

:return: the solidity signature
"""
if self._solidity_signature is None:
Expand Down
55 changes: 41 additions & 14 deletions slither/utils/type.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import math
from typing import List, Union
from typing import List, Union, Set

from slither.core.solidity_types import ArrayType, MappingType, ElementaryType, UserDefinedType
from slither.core.solidity_types.type import Type
from slither.core.variables.variable import Variable


def _convert_type_for_solidity_signature_to_string(types: Union[Type, List[Type]]) -> str:
def _convert_type_for_solidity_signature_to_string(
types: Union[Type, List[Type]], seen: Set[Type]
) -> str:
if isinstance(types, Type):
# Array might be struct, so we need to go again through the conversion here
# We could have this logic in convert_type_for_solidity_signature
Expand All @@ -15,8 +17,10 @@ def _convert_type_for_solidity_signature_to_string(types: Union[Type, List[Type]
# Which is currently not supported. This comes down to (uint, uint)[] not being possible in Solidity
# While having an array of a struct of two uint leads to a (uint, uint)[] signature
if isinstance(types, ArrayType):
underlying_type = convert_type_for_solidity_signature(types.type)
underlying_type_str = _convert_type_for_solidity_signature_to_string(underlying_type)
underlying_type = convert_type_for_solidity_signature(types.type, seen)
underlying_type_str = _convert_type_for_solidity_signature_to_string(
underlying_type, seen
)
return underlying_type_str + "[]"

return str(types)
Expand All @@ -26,24 +30,43 @@ def _convert_type_for_solidity_signature_to_string(types: Union[Type, List[Type]
ret = "("
for underlying_type in types:
if first_item:
ret += _convert_type_for_solidity_signature_to_string(underlying_type)
ret += _convert_type_for_solidity_signature_to_string(underlying_type, seen)
else:
ret += "," + _convert_type_for_solidity_signature_to_string(underlying_type)
ret += "," + _convert_type_for_solidity_signature_to_string(underlying_type, seen)
first_item = False

ret += ")"
return ret


def convert_type_for_solidity_signature_to_string(t: Type) -> str:
types = convert_type_for_solidity_signature(t)
return _convert_type_for_solidity_signature_to_string(types)
seen: Set[Type] = set()
types = convert_type_for_solidity_signature(t, seen)
return _convert_type_for_solidity_signature_to_string(types, seen)


def convert_type_for_solidity_signature(t: Type) -> Union[Type, List[Type]]:
def convert_type_for_solidity_signature(t: Type, seen: Set[Type]) -> Union[Type, List[Type]]:
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract, Enum, Structure

# Solidity allows recursive type for structure definition if its not used in public /external
# When this happens we can reach an infinite loop. If we detect a loop, we just stop converting the underlying type
# This is ok, because it wont happen for public/external function
#
# contract A{
#
# struct St{
# St[] a;
# uint b;
# }
#
# function f(St memory s) internal{}
#
# }
if t in seen:
return t
seen.add(t)

if isinstance(t, UserDefinedType):
underlying_type = t.type
if isinstance(underlying_type, Contract):
Expand All @@ -61,21 +84,24 @@ def convert_type_for_solidity_signature(t: Type) -> Union[Type, List[Type]]:
if isinstance(underlying_type, Structure):
# We can't have recursive types for structure, so recursion is ok here
types = [
convert_type_for_solidity_signature(x.type) for x in underlying_type.elems_ordered
convert_type_for_solidity_signature(x.type, seen)
for x in underlying_type.elems_ordered
]
return types

return t


def _export_nested_types_from_variable(current_type: Type, ret: List[Type]) -> None:
def _export_nested_types_from_variable(
current_type: Type, ret: List[Type], seen: Set[Type]
) -> None:
"""
Export the list of nested types (mapping/array)
:param variable:
:return: list(Type)
"""
if isinstance(current_type, MappingType):
underlying_type = convert_type_for_solidity_signature(current_type.type_from)
underlying_type = convert_type_for_solidity_signature(current_type.type_from, seen)
if isinstance(underlying_type, list):
ret.extend(underlying_type)
else:
Expand All @@ -87,7 +113,7 @@ def _export_nested_types_from_variable(current_type: Type, ret: List[Type]) -> N
next_type = current_type.type
else:
return
_export_nested_types_from_variable(next_type, ret)
_export_nested_types_from_variable(next_type, ret, seen)


def export_nested_types_from_variable(variable: Variable) -> List[Type]:
Expand All @@ -97,7 +123,8 @@ def export_nested_types_from_variable(variable: Variable) -> List[Type]:
:return: list(Type)
"""
l: List[Type] = []
_export_nested_types_from_variable(variable.type, l)
seen: Set[Type] = set()
_export_nested_types_from_variable(variable.type, l, seen)
return l


Expand Down
12 changes: 12 additions & 0 deletions tests/function_ids/rec_struct-0.8.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
contract A{

struct St{
St[] a;
uint b;
}

function f(St memory s) internal{
f(s);
}

}
9 changes: 9 additions & 0 deletions tests/test_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from crytic_compile import CryticCompile
from crytic_compile.platform.solc_standard_json import SolcStandardJson
from solc_select import solc_select

from slither import Slither
from slither.detectors import all_detectors
Expand Down Expand Up @@ -41,3 +42,11 @@ def test_collision():
def test_cycle():
slither = Slither("./tests/test_cyclic_import/a.sol")
_run_all_detectors(slither)


def test_funcion_id_rec_structure():
solc_select.switch_global_version("0.8.0", always_install=True)
slither = Slither("./tests/function_ids/rec_struct-0.8.sol")
for compilation_unit in slither.compilation_units:
for function in compilation_unit.functions:
assert function.solidity_signature