Merge branch 'dev' into dev-fix-erc20-burnable-prop

pull/954/head
Josselin 3 years ago
commit 99ad8ada11
  1. 18
      README.md
  2. 2
      setup.py
  3. 6
      slither/core/compilation_unit.py
  4. 44
      slither/core/declarations/contract.py
  5. 71
      slither/core/declarations/custom_error.py
  6. 12
      slither/core/declarations/custom_error_contract.py
  7. 6
      slither/core/declarations/custom_error_top_level.py
  8. 9
      slither/core/declarations/function.py
  9. 19
      slither/core/declarations/solidity_variables.py
  10. 2
      slither/detectors/erc/unindexed_event_parameters.py
  11. 76
      slither/detectors/statements/calls_in_loop.py
  12. 83
      slither/detectors/statements/costly_operations_in_loop.py
  13. 4
      slither/detectors/statements/incorrect_strict_equality.py
  14. 11
      slither/printers/call/call_graph.py
  15. 3
      slither/printers/inheritance/inheritance.py
  16. 22
      slither/slithir/convert.py
  17. 3
      slither/slithir/operations/member.py
  18. 2
      slither/slithir/tmp_operations/tmp_call.py
  19. 8
      slither/slithir/utils/ssa.py
  20. 30
      slither/solc_parsing/declarations/contract.py
  21. 101
      slither/solc_parsing/declarations/custom_error.py
  22. 14
      slither/solc_parsing/declarations/function.py
  23. 21
      slither/solc_parsing/expressions/find_variable.py
  24. 22
      slither/solc_parsing/slither_compilation_unit_solc.py
  25. 22
      slither/solc_parsing/solidity_types/type_parsing.py
  26. 31
      slither/solc_parsing/yul/parse_yul.py
  27. 6
      slither/tools/flattening/flattening.py
  28. BIN
      tests/ast-parsing/compile/assembly-0.8.10-compact.zip
  29. BIN
      tests/ast-parsing/compile/assembly-0.8.8-compact.zip
  30. BIN
      tests/ast-parsing/compile/assembly-0.8.9-compact.zip
  31. BIN
      tests/ast-parsing/compile/assignment-0.8.10-compact.zip
  32. BIN
      tests/ast-parsing/compile/assignment-0.8.8-compact.zip
  33. BIN
      tests/ast-parsing/compile/assignment-0.8.9-compact.zip
  34. BIN
      tests/ast-parsing/compile/binaryoperation-0.8.10-compact.zip
  35. BIN
      tests/ast-parsing/compile/binaryoperation-0.8.8-compact.zip
  36. BIN
      tests/ast-parsing/compile/binaryoperation-0.8.9-compact.zip
  37. BIN
      tests/ast-parsing/compile/break-0.8.10-compact.zip
  38. BIN
      tests/ast-parsing/compile/break-0.8.8-compact.zip
  39. BIN
      tests/ast-parsing/compile/break-0.8.9-compact.zip
  40. BIN
      tests/ast-parsing/compile/call_to_variable-0.8.10-compact.zip
  41. BIN
      tests/ast-parsing/compile/call_to_variable-0.8.8-compact.zip
  42. BIN
      tests/ast-parsing/compile/call_to_variable-0.8.9-compact.zip
  43. BIN
      tests/ast-parsing/compile/comment-0.8.10-compact.zip
  44. BIN
      tests/ast-parsing/compile/comment-0.8.8-compact.zip
  45. BIN
      tests/ast-parsing/compile/comment-0.8.9-compact.zip
  46. BIN
      tests/ast-parsing/compile/conditional-0.8.10-compact.zip
  47. BIN
      tests/ast-parsing/compile/conditional-0.8.8-compact.zip
  48. BIN
      tests/ast-parsing/compile/conditional-0.8.9-compact.zip
  49. BIN
      tests/ast-parsing/compile/continue-0.8.10-compact.zip
  50. BIN
      tests/ast-parsing/compile/continue-0.8.8-compact.zip
  51. BIN
      tests/ast-parsing/compile/continue-0.8.9-compact.zip
  52. BIN
      tests/ast-parsing/compile/contract-0.8.10-compact.zip
  53. BIN
      tests/ast-parsing/compile/contract-0.8.8-compact.zip
  54. BIN
      tests/ast-parsing/compile/contract-0.8.9-compact.zip
  55. BIN
      tests/ast-parsing/compile/custom_error-0.4.0-legacy.zip
  56. BIN
      tests/ast-parsing/compile/custom_error-0.4.1-legacy.zip
  57. BIN
      tests/ast-parsing/compile/custom_error-0.4.10-legacy.zip
  58. BIN
      tests/ast-parsing/compile/custom_error-0.4.11-legacy.zip
  59. BIN
      tests/ast-parsing/compile/custom_error-0.4.12-compact.zip
  60. BIN
      tests/ast-parsing/compile/custom_error-0.4.12-legacy.zip
  61. BIN
      tests/ast-parsing/compile/custom_error-0.4.13-compact.zip
  62. BIN
      tests/ast-parsing/compile/custom_error-0.4.13-legacy.zip
  63. BIN
      tests/ast-parsing/compile/custom_error-0.4.14-compact.zip
  64. BIN
      tests/ast-parsing/compile/custom_error-0.4.14-legacy.zip
  65. BIN
      tests/ast-parsing/compile/custom_error-0.4.15-compact.zip
  66. BIN
      tests/ast-parsing/compile/custom_error-0.4.15-legacy.zip
  67. BIN
      tests/ast-parsing/compile/custom_error-0.4.16-compact.zip
  68. BIN
      tests/ast-parsing/compile/custom_error-0.4.16-legacy.zip
  69. BIN
      tests/ast-parsing/compile/custom_error-0.4.17-compact.zip
  70. BIN
      tests/ast-parsing/compile/custom_error-0.4.17-legacy.zip
  71. BIN
      tests/ast-parsing/compile/custom_error-0.4.18-compact.zip
  72. BIN
      tests/ast-parsing/compile/custom_error-0.4.18-legacy.zip
  73. BIN
      tests/ast-parsing/compile/custom_error-0.4.19-compact.zip
  74. BIN
      tests/ast-parsing/compile/custom_error-0.4.19-legacy.zip
  75. BIN
      tests/ast-parsing/compile/custom_error-0.4.2-legacy.zip
  76. BIN
      tests/ast-parsing/compile/custom_error-0.4.20-compact.zip
  77. BIN
      tests/ast-parsing/compile/custom_error-0.4.20-legacy.zip
  78. BIN
      tests/ast-parsing/compile/custom_error-0.4.21-compact.zip
  79. BIN
      tests/ast-parsing/compile/custom_error-0.4.21-legacy.zip
  80. BIN
      tests/ast-parsing/compile/custom_error-0.4.22-compact.zip
  81. BIN
      tests/ast-parsing/compile/custom_error-0.4.22-legacy.zip
  82. BIN
      tests/ast-parsing/compile/custom_error-0.4.23-compact.zip
  83. BIN
      tests/ast-parsing/compile/custom_error-0.4.23-legacy.zip
  84. BIN
      tests/ast-parsing/compile/custom_error-0.4.24-compact.zip
  85. BIN
      tests/ast-parsing/compile/custom_error-0.4.24-legacy.zip
  86. BIN
      tests/ast-parsing/compile/custom_error-0.4.25-compact.zip
  87. BIN
      tests/ast-parsing/compile/custom_error-0.4.25-legacy.zip
  88. BIN
      tests/ast-parsing/compile/custom_error-0.4.26-compact.zip
  89. BIN
      tests/ast-parsing/compile/custom_error-0.4.26-legacy.zip
  90. BIN
      tests/ast-parsing/compile/custom_error-0.4.3-legacy.zip
  91. BIN
      tests/ast-parsing/compile/custom_error-0.4.4-legacy.zip
  92. BIN
      tests/ast-parsing/compile/custom_error-0.4.5-legacy.zip
  93. BIN
      tests/ast-parsing/compile/custom_error-0.4.6-legacy.zip
  94. BIN
      tests/ast-parsing/compile/custom_error-0.4.7-legacy.zip
  95. BIN
      tests/ast-parsing/compile/custom_error-0.4.8-legacy.zip
  96. BIN
      tests/ast-parsing/compile/custom_error-0.4.9-legacy.zip
  97. BIN
      tests/ast-parsing/compile/custom_error-0.5.0-compact.zip
  98. BIN
      tests/ast-parsing/compile/custom_error-0.5.0-legacy.zip
  99. BIN
      tests/ast-parsing/compile/custom_error-0.5.1-compact.zip
  100. BIN
      tests/ast-parsing/compile/custom_error-0.5.1-legacy.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -199,7 +199,7 @@ Feel free to stop by our [Slack channel](https://empireslacking.herokuapp.com) (
* The [Detector documentation](https://github.com/trailofbits/slither/wiki/Adding-a-new-detector) describes how to write a new vulnerability analyses.
* The [API documentation](https://github.com/trailofbits/slither/wiki/API-examples) describes the methods and objects available for custom analyses.
* The [API documentation](https://github.com/crytic/slither/wiki/Python-API) describes the methods and objects available for custom analyses.
* The [SlithIR documentation](https://github.com/trailofbits/slither/wiki/SlithIR) describes the SlithIR intermediate representation.
@ -214,12 +214,14 @@ Slither is licensed and distributed under the AGPLv3 license. [Contact us](mailt
- [Slither: A Static Analysis Framework For Smart Contracts](https://arxiv.org/abs/1908.09878), Josselin Feist, Gustavo Grieco, Alex Groce - WETSEB '19
### External publications
- [ReJection: A AST-Based Reentrancy Vulnerability Detection Method](https://www.researchgate.net/publication/339354823_ReJection_A_AST-Based_Reentrancy_Vulnerability_Detection_Method), Rui Ma, Zefeng Jian, Guangyuan Chen, Ke Ma, Yujia Chen - CTCIS 19
- [MPro: Combining Static and Symbolic Analysis forScalable Testing of Smart Contract](https://arxiv.org/pdf/1911.00570.pdf), William Zhang, Sebastian Banescu, Leodardo Pasos, Steven Stewart, Vijay Ganesh - ISSRE 2019
- [ETHPLOIT: From Fuzzing to Efficient Exploit Generation against Smart Contracts](https://wcventure.github.io/FuzzingPaper/Paper/SANER20_ETHPLOIT.pdf), Qingzhao Zhang, Yizhuo Wang, Juanru Li, Siqi Ma - SANER 20
- [Verification of Ethereum Smart Contracts: A Model Checking Approach](http://www.ijmlc.org/vol10/977-AM0059.pdf), Tam Bang, Hoang H Nguyen, Dung Nguyen, Toan Trieu, Tho Quan - IJMLC 20
- [Smart Contract Repair](https://arxiv.org/pdf/1912.05823.pdf), Xiao Liang Yu, Omar Al-Bataineh, David Lo, Abhik Roychoudhury - TOSEM 20
- [Demystifying Loops in Smart Contracts](https://www.microsoft.com/en-us/research/uploads/prod/2020/08/loops_solidity__camera_ready-5f3fec3f15c69.pdf), Ben Mariano, Yanju Chen, Yu Feng, Shuvendu Lahiri, Isil Dillig - ASE 20
- [Trace-Based Dynamic Gas Estimation of Loops in Smart Contracts](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9268144), Chunmiao Li, Shijie Nie, Yang Cao, Yijun Yu, Zhenjiang Hu - IEEE Open J. Comput. Soc. 1 (2020)
Title | Usage | Authors | Venue
--- | --- | --- | ---
[ReJection: A AST-Based Reentrancy Vulnerability Detection Method](https://www.researchgate.net/publication/339354823_ReJection_A_AST-Based_Reentrancy_Vulnerability_Detection_Method) | AST-based analysis built on top of Slither | Rui Ma, Zefeng Jian, Guangyuan Chen, Ke Ma, Yujia Chen | CTCIS 19
[MPro: Combining Static and Symbolic Analysis forScalable Testing of Smart Contract](https://arxiv.org/pdf/1911.00570.pdf) | Leverage data dependency through Slither | William Zhang, Sebastian Banescu, Leodardo Pasos, Steven Stewart, Vijay Ganesh | ISSRE 2019
[ETHPLOIT: From Fuzzing to Efficient Exploit Generation against Smart Contracts](https://wcventure.github.io/FuzzingPaper/Paper/SANER20_ETHPLOIT.pdf) | Leverage data dependency through Slither | Qingzhao Zhang, Yizhuo Wang, Juanru Li, Siqi Ma | SANER 20
[Verification of Ethereum Smart Contracts: A Model Checking Approach](http://www.ijmlc.org/vol10/977-AM0059.pdf) | Symbolic execution built on top of Slither’s CFG | Tam Bang, Hoang H Nguyen, Dung Nguyen, Toan Trieu, Tho Quan | IJMLC 20
[Smart Contract Repair](https://arxiv.org/pdf/1912.05823.pdf) | Rely on Slither’s vulnerabilities detectors | Xiao Liang Yu, Omar Al-Bataineh, David Lo, Abhik Roychoudhury | TOSEM 20
[Demystifying Loops in Smart Contracts](https://www.microsoft.com/en-us/research/uploads/prod/2020/08/loops_solidity__camera_ready-5f3fec3f15c69.pdf) | Leverage data dependency through Slither | Ben Mariano, Yanju Chen, Yu Feng, Shuvendu Lahiri, Isil Dillig | ASE 20
[Trace-Based Dynamic Gas Estimation of Loops in Smart Contracts](https://ieeexplore.ieee.org/stamp/stamp.jsp?arnumber=9268144) | Use Slither’s CFG to detect loops | Chunmiao Li, Shijie Nie, Yang Cao, Yijun Yu, Zhenjiang Hu | IEEE Open J. Comput. Soc. 1 (2020)
If you are using Slither on an academic work, consider applying to the [Crytic $10k Research Prize](https://blog.trailofbits.com/2019/11/13/announcing-the-crytic-10k-research-prize/).

@ -16,7 +16,7 @@ setup(
],
dependency_links=["git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile"],
license="AGPL-3.0",
long_description=open("README.md").read(),
long_description=open("README.md", "r", encoding="utf-8").read(),
entry_points={
"console_scripts": [
"slither = slither.__main__:main",

@ -13,6 +13,7 @@ from slither.core.declarations import (
Function,
Modifier,
)
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
@ -40,6 +41,7 @@ class SlitherCompilationUnit(Context):
self._functions_top_level: List[FunctionTopLevel] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._custom_errors: List[CustomError] = []
self._all_functions: Set[Function] = set()
self._all_modifiers: Set[Modifier] = set()
@ -210,6 +212,10 @@ class SlitherCompilationUnit(Context):
def functions_top_level(self) -> List[FunctionTopLevel]:
return self._functions_top_level
@property
def custom_errors(self) -> List[CustomError]:
return self._custom_errors
# endregion
###################################################################################
###################################################################################

@ -11,7 +11,7 @@ from slither.core.cfg.scope import Scope
from slither.core.solidity_types.type import Type
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.declarations.function import Function, FunctionType
from slither.core.declarations.function import Function, FunctionType, FunctionLanguage
from slither.utils.erc import (
ERC20_signatures,
ERC165_signatures,
@ -38,6 +38,7 @@ if TYPE_CHECKING:
from slither.core.variables.variable import Variable
from slither.core.variables.state_variable import StateVariable
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations.custom_error_contract import CustomErrorContract
LOGGER = logging.getLogger("Contract")
@ -68,6 +69,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
self._modifiers: Dict[str, "Modifier"] = {}
self._functions: Dict[str, "FunctionContract"] = {}
self._linearizedBaseContracts: List[int] = []
self._custom_errors: Dict[str:"CustomErrorContract"] = {}
# The only str is "*"
self._using_for: Dict[Union[str, Type], List[str]] = {}
@ -242,6 +244,38 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def using_for(self) -> Dict[Union[str, Type], List[str]]:
return self._using_for
# endregion
###################################################################################
###################################################################################
# region Custom Errors
###################################################################################
###################################################################################
@property
def custom_errors(self) -> List["CustomErrorContract"]:
"""
list(CustomErrorContract): List of the contract's custom errors
"""
return list(self._custom_errors.values())
@property
def custom_errors_inherited(self) -> List["CustomErrorContract"]:
"""
list(CustomErrorContract): List of the inherited custom errors
"""
return [s for s in self.custom_errors if s.contract != self]
@property
def custom_errors_declared(self) -> List["CustomErrorContract"]:
"""
list(CustomErrorContract): List of the custom errors declared within the contract (not inherited)
"""
return [s for s in self.custom_errors if s.contract == self]
@property
def custom_errors_as_dict(self) -> Dict[str, "CustomErrorContract"]:
return self._custom_errors
# endregion
###################################################################################
###################################################################################
@ -506,7 +540,7 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
def available_elements_from_inheritances(
self,
elements: Dict[str, "Function"],
getter_available: Callable[["Contract"], List["Function"]],
getter_available: Callable[["Contract"], List["FunctionContract"]],
) -> Dict[str, "Function"]:
"""
@ -517,14 +551,16 @@ class Contract(SourceMapping): # pylint: disable=too-many-public-methods
# keep track of the contracts visited
# to prevent an ovveride due to multiple inheritance of the same contract
# A is B, C, D is C, --> the second C was already seen
inherited_elements: Dict[str, "Function"] = {}
inherited_elements: Dict[str, "FunctionContract"] = {}
accessible_elements = {}
contracts_visited = []
for father in self.inheritance_reverse:
functions: Dict[str, "Function"] = {
functions: Dict[str, "FunctionContract"] = {
v.full_name: v
for v in getter_available(father)
if v.contract not in contracts_visited
and v.function_language
!= FunctionLanguage.Yul # Yul functions are not propagated in the inheritance
}
contracts_visited.append(father)
inherited_elements.update(functions)

@ -0,0 +1,71 @@
from typing import List, TYPE_CHECKING, Optional, Type, Union
from slither.core.solidity_types import UserDefinedType
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
class CustomError(SourceMapping):
def __init__(self, compilation_unit: "SlitherCompilationUnit"):
super().__init__()
self._name: str = ""
self._parameters: List[LocalVariable] = []
self._compilation_unit = compilation_unit
self._solidity_signature: Optional[str] = None
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, new_name: str) -> None:
self._name = new_name
@property
def parameters(self) -> List[LocalVariable]:
return self._parameters
def add_parameters(self, p: "LocalVariable"):
self._parameters.append(p)
@property
def compilation_unit(self) -> "SlitherCompilationUnit":
return self._compilation_unit
# region Signature
###################################################################################
###################################################################################
@staticmethod
def _convert_type_for_solidity_signature(t: Optional[Union[Type, List[Type]]]):
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return "address"
return str(t)
@property
def solidity_signature(self) -> str:
"""
Return a signature following the Solidity Standard
Contract and converted into address
:return: the solidity signature
"""
if self._solidity_signature is None:
parameters = [
self._convert_type_for_solidity_signature(x.type) for x in self.parameters
]
self._solidity_signature = self.name + "(" + ",".join(parameters) + ")"
return self._solidity_signature
# endregion
###################################################################################
###################################################################################
def __str__(self):
return "revert " + self.solidity_signature

@ -0,0 +1,12 @@
from slither.core.children.child_contract import ChildContract
from slither.core.declarations.custom_error import CustomError
class CustomErrorContract(CustomError, ChildContract):
def is_declared_by(self, contract):
"""
Check if the element is declared by the contract
:param contract:
:return:
"""
return self.contract == contract

@ -0,0 +1,6 @@
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.top_level import TopLevel
class CustomErrorTopLevel(CustomError, TopLevel):
pass

@ -104,6 +104,12 @@ def _filter_state_variables_written(expressions: List["Expression"]):
return ret
class FunctionLanguage(Enum):
Solidity = 0
Yul = 1
Vyper = 2
class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-public-methods
"""
Function class
@ -207,6 +213,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
# Assume we are analyzing Solidty by default
self.function_language: FunctionLanguage = FunctionLanguage.Solidity
###################################################################################
###################################################################################
# region General properties

@ -2,6 +2,7 @@
from typing import List, Dict, Union, TYPE_CHECKING
from slither.core.context.context import Context
from slither.core.declarations.custom_error import CustomError
from slither.core.solidity_types import ElementaryType, TypeInformation
from slither.exceptions import SlitherException
@ -42,6 +43,7 @@ SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
"require(bool,string)": [],
"revert()": [],
"revert(string)": [],
"revert ": [],
"addmod(uint256,uint256,uint256)": ["uint256"],
"mulmod(uint256,uint256,uint256)": ["uint256"],
"keccak256()": ["bytes32"],
@ -184,3 +186,20 @@ class SolidityFunction:
def __hash__(self):
return hash(self.name)
class SolidityCustomRevert(SolidityFunction):
def __init__(self, custom_error: CustomError): # pylint: disable=super-init-not-called
self._name = "revert " + custom_error.solidity_signature
self._custom_error = custom_error
self._return_type: List[Union[TypeInformation, ElementaryType]] = []
def __eq__(self, other):
return (
self.__class__ == other.__class__
and self.name == other.name
and self._custom_error == other._custom_error
)
def __hash__(self):
return hash(hash(self.name) + hash(self._custom_error))

@ -16,7 +16,7 @@ class UnindexedERC20EventParameters(AbstractDetector):
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#unindexed-erc20-event-parameters"
WIKI_TITLE = "Unindexed ERC20 event oarameters"
WIKI_TITLE = "Unindexed ERC20 event parameters"
WIKI_DESCRIPTION = "Detects whether events defined by the `ERC20` specification that should have some parameters as `indexed` are missing the `indexed` keyword."
# region wiki_exploit_scenario

@ -1,14 +1,51 @@
from slither.core.cfg.node import NodeType
from typing import List
from slither.core.cfg.node import NodeType, Node
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Contract
from slither.utils.output import Output
from slither.slithir.operations import (
HighLevelCall,
LibraryCall,
LowLevelCall,
Send,
Transfer,
InternalCall,
)
def detect_call_in_loop(contract: Contract) -> List[Node]:
ret: List[Node] = []
for f in contract.functions_entry_points:
if f.is_implemented:
call_in_loop(f.entry_point, 0, [], ret)
return ret
def call_in_loop(node: Node, in_loop_counter: int, visited: List[Node], ret: List[Node]) -> None:
if node in visited:
return
# shared visited
visited.append(node)
if node.type == NodeType.STARTLOOP:
in_loop_counter += 1
elif node.type == NodeType.ENDLOOP:
in_loop_counter -= 1
if in_loop_counter > 0:
for ir in node.all_slithir_operations():
if isinstance(ir, (LowLevelCall, HighLevelCall, Send, Transfer)):
if isinstance(ir, LibraryCall):
continue
ret.append(ir.node)
if isinstance(ir, (InternalCall)):
call_in_loop(ir.function.entry_point, in_loop_counter, visited, ret)
for son in node.sons:
call_in_loop(son, in_loop_counter, visited, ret)
class MultipleCallsInLoop(AbstractDetector):
ARGUMENT = "calls-loop"
@ -45,42 +82,11 @@ If one of the destinations has a fallback function that reverts, `bad` will alwa
WIKI_RECOMMENDATION = "Favor [pull over push](https://github.com/ethereum/wiki/wiki/Safety#favor-pull-over-push-for-external-calls) strategy for external calls."
@staticmethod
def call_in_loop(node, in_loop, visited, ret):
if node in visited:
return
# shared visited
visited.append(node)
if node.type == NodeType.STARTLOOP:
in_loop = True
elif node.type == NodeType.ENDLOOP:
in_loop = False
if in_loop:
for ir in node.irs:
if isinstance(ir, (LowLevelCall, HighLevelCall, Send, Transfer)):
if isinstance(ir, LibraryCall):
continue
ret.append(node)
for son in node.sons:
MultipleCallsInLoop.call_in_loop(son, in_loop, visited, ret)
@staticmethod
def detect_call_in_loop(contract):
ret = []
for f in contract.functions + contract.modifiers:
if f.contract_declarer == contract and f.is_implemented:
MultipleCallsInLoop.call_in_loop(f.entry_point, False, [], ret)
return ret
def _detect(self):
def _detect(self) -> List[Output]:
""""""
results = []
results: List[Output] = []
for c in self.compilation_unit.contracts_derived:
values = self.detect_call_in_loop(c)
values = detect_call_in_loop(c)
for node in values:
func = node.function

@ -1,7 +1,45 @@
from slither.core.cfg.node import NodeType
from slither.core.solidity_types.array_type import ArrayType
from slither.core.solidity_types.mapping_type import MappingType
from typing import List
from slither.core.cfg.node import NodeType, Node
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations import Contract
from slither.utils.output import Output
from slither.slithir.operations import InternalCall, OperationWithLValue
from slither.core.variables.state_variable import StateVariable
def detect_costly_operations_in_loop(contract: Contract) -> List[Node]:
ret: List[Node] = []
for f in contract.functions_entry_points:
if f.is_implemented:
costly_operations_in_loop(f.entry_point, 0, [], ret)
return ret
def costly_operations_in_loop(
node: Node, in_loop_counter: int, visited: List[Node], ret: List[Node]
) -> None:
if node in visited:
return
# shared visited
visited.append(node)
if node.type == NodeType.STARTLOOP:
in_loop_counter += 1
elif node.type == NodeType.ENDLOOP:
in_loop_counter -= 1
if in_loop_counter > 0:
for ir in node.all_slithir_operations():
# Ignore Array/Mapping/Struct types for now
if isinstance(ir, OperationWithLValue) and isinstance(ir.lvalue, StateVariable):
ret.append(ir.node)
break
if isinstance(ir, (InternalCall)):
costly_operations_in_loop(ir.function.entry_point, in_loop_counter, visited, ret)
for son in node.sons:
costly_operations_in_loop(son, in_loop_counter, visited, ret)
class CostlyOperationsInLoop(AbstractDetector):
@ -49,44 +87,11 @@ Incrementing `state_variable` in a loop incurs a lot of gas because of expensive
WIKI_RECOMMENDATION = "Use a local variable to hold the loop computation result."
@staticmethod
def costly_operations_in_loop(node, in_loop, visited, ret):
if node in visited:
return
# shared visited
visited.append(node)
if node.type == NodeType.STARTLOOP:
in_loop = True
elif node.type == NodeType.ENDLOOP:
in_loop = False
if in_loop:
sv_written = node.state_variables_written
for sv in sv_written:
# Ignore Array/Mapping/Struct types for now
if isinstance(sv.type, (ArrayType, MappingType)):
continue
ret.append(node)
break
for son in node.sons:
CostlyOperationsInLoop.costly_operations_in_loop(son, in_loop, visited, ret)
@staticmethod
def detect_costly_operations_in_loop(contract):
ret = []
for f in contract.functions + contract.modifiers:
if f.contract_declarer == contract and f.is_implemented:
CostlyOperationsInLoop.costly_operations_in_loop(f.entry_point, False, [], ret)
return ret
def _detect(self):
def _detect(self) -> List[Output]:
""""""
results = []
results: List[Output] = []
for c in self.compilation_unit.contracts_derived:
values = self.detect_costly_operations_in_loop(c)
values = detect_costly_operations_in_loop(c)
for node in values:
func = node.function
info = [func, " has costly operations inside a loop:\n"]

@ -5,6 +5,7 @@
from slither.analyses.data_dependency.data_dependency import is_dependent_ssa
from slither.core.declarations import Function
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import (
Assignment,
@ -106,6 +107,9 @@ contract Crowdsale{
taints += self.sources_taint
for func in funcs:
# Disable the detector on top level function until we have good taint on those
if isinstance(func, FunctionTopLevel):
continue
for node in func.nodes:
for ir in node.irs_ssa:

@ -218,7 +218,12 @@ class PrinterCallGraph(AbstractPrinter):
all_contracts_filename = ""
if not filename.endswith(".dot"):
all_contracts_filename = f"{filename}.all_contracts.call-graph.dot"
if filename in ("", "."):
filename = ""
else:
filename += "."
all_contracts_filename = f"{filename}all_contracts.call-graph.dot"
if filename == ".dot":
all_contracts_filename = "all_contracts.dot"
@ -227,7 +232,7 @@ class PrinterCallGraph(AbstractPrinter):
with open(all_contracts_filename, "w", encoding="utf8") as f:
info += f"Call Graph: {all_contracts_filename}\n"
# Avoid dupplicate funcitons due to different compilation unit
# Avoid duplicate functions due to different compilation unit
all_functionss = [
compilation_unit.functions for compilation_unit in self.slither.compilation_units
]
@ -242,7 +247,7 @@ class PrinterCallGraph(AbstractPrinter):
results.append((all_contracts_filename, content))
for derived_contract in self.slither.contracts_derived:
derived_output_filename = f"{filename}.{derived_contract.name}.call-graph.dot"
derived_output_filename = f"{filename}{derived_contract.name}.call-graph.dot"
with open(derived_output_filename, "w", encoding="utf8") as f:
info += f"Call Graph: {derived_output_filename}\n"
content = "\n".join(

@ -30,9 +30,6 @@ class PrinterInheritance(AbstractPrinter):
"""
info = "Inheritance\n"
if not self.contracts:
return []
info += blue("Child_Contract -> ") + green("Immediate_Base_Contracts")
info += green(" [Not_Immediate_Base_Contracts]")

@ -13,8 +13,10 @@ from slither.core.declarations import (
SolidityVariableComposed,
Structure,
)
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
from slither.core.declarations.solidity_variables import SolidityCustomRevert
from slither.core.expressions import Identifier, Literal
from slither.core.solidity_types import (
ArrayType,
@ -621,7 +623,19 @@ def propagate_types(ir, node: "Node"): # pylint: disable=too-many-locals
b.set_expression(ir.expression)
b.set_node(ir.node)
return b
if ir.variable_right == "selector" and isinstance(ir.variable_left.type, Function):
if ir.variable_right == "selector" and isinstance(ir.variable_left, (CustomError)):
assignment = Assignment(
ir.lvalue,
Constant(str(get_function_id(ir.variable_left.solidity_signature))),
ElementaryType("bytes4"),
)
assignment.set_expression(ir.expression)
assignment.set_node(ir.node)
assignment.lvalue.set_type(ElementaryType("bytes4"))
return assignment
if ir.variable_right == "selector" and isinstance(
ir.variable_left.type, (Function)
):
assignment = Assignment(
ir.lvalue,
Constant(str(get_function_id(ir.variable_left.type.full_name))),
@ -941,6 +955,12 @@ def extract_tmp_call(ins: TmpCall, contract: Optional[Contract]): # pylint: dis
s.set_expression(ins.expression)
return s
if isinstance(ins.called, CustomError):
sol_function = SolidityCustomRevert(ins.called)
s = SolidityCall(sol_function, ins.nbr_arguments, ins.lvalue, ins.type_call)
s.set_expression(ins.expression)
return s
if isinstance(ins.ori, TmpNewElementaryType):
n = NewElementaryType(ins.ori.type, ins.lvalue)
n.set_expression(ins.expression)

@ -1,4 +1,5 @@
from slither.core.declarations import Contract, Function
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.enum import Enum
from slither.core.declarations.solidity_import_placeholder import SolidityImportPlaceHolder
from slither.slithir.operations.lvalue import OperationWithLValue
@ -21,7 +22,7 @@ class Member(OperationWithLValue):
# }
# }
assert is_valid_rvalue(variable_left) or isinstance(
variable_left, (Contract, Enum, Function, SolidityImportPlaceHolder)
variable_left, (Contract, Enum, Function, CustomError, SolidityImportPlaceHolder)
)
assert isinstance(variable_right, Constant)

@ -5,6 +5,7 @@ from slither.core.declarations import (
SolidityFunction,
Structure,
)
from slither.core.declarations.custom_error import CustomError
from slither.core.variables.variable import Variable
from slither.slithir.operations.lvalue import OperationWithLValue
@ -20,6 +21,7 @@ class TmpCall(OperationWithLValue): # pylint: disable=too-many-instance-attribu
SolidityFunction,
Structure,
Event,
CustomError,
),
)
super().__init__()

@ -284,7 +284,13 @@ def generate_ssa_irs(
if isinstance(new_ir.rvalue, ReferenceVariable):
refers_to = new_ir.rvalue.points_to_origin
new_ir.lvalue.add_refers_to(refers_to)
else:
# Discard Constant
# This can happen on yul, like
# assembly { var.slot = some_value }
# Here we do not keep track of the references as we do not track
# such low level manipulation
# However we could extend our storage model to do so in the future
elif not isinstance(new_ir.rvalue, Constant):
new_ir.lvalue.add_refers_to(new_ir.rvalue)
for succ in node.dominator_successors:

@ -3,8 +3,10 @@ from typing import List, Dict, Callable, TYPE_CHECKING, Union, Set
from slither.core.declarations import Modifier, Event, EnumContract, StructureContract, Function
from slither.core.declarations.contract import Contract
from slither.core.declarations.custom_error_contract import CustomErrorContract
from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.event import EventSolc
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.modifier import ModifierSolc
@ -35,15 +37,17 @@ class ContractSolc:
self._modifiersNotParsed: List[Dict] = []
self._functions_no_params: List[FunctionSolc] = []
self._modifiers_no_params: List[ModifierSolc] = []
self._eventsNotParsed: List[EventSolc] = []
self._eventsNotParsed: List[Dict] = []
self._variablesNotParsed: List[Dict] = []
self._enumsNotParsed: List[Dict] = []
self._structuresNotParsed: List[Dict] = []
self._usingForNotParsed: List[Dict] = []
self._customErrorParsed: List[Dict] = []
self._functions_parser: List[FunctionSolc] = []
self._modifiers_parser: List[ModifierSolc] = []
self._structures_parser: List[StructureContractSolc] = []
self._custom_errors_parser: List[CustomErrorSolc] = []
self._is_analyzed: bool = False
@ -246,6 +250,8 @@ class ContractSolc:
self._structuresNotParsed.append(item)
elif item[self.get_key()] == "UsingForDirective":
self._usingForNotParsed.append(item)
elif item[self.get_key()] == "ErrorDefinition":
self._customErrorParsed.append(item)
else:
raise ParsingError("Unknown contract item: " + item[self.get_key()])
return
@ -268,6 +274,23 @@ class ContractSolc:
self._parse_struct(struct)
self._structuresNotParsed = None
def _parse_custom_error(self, custom_error: Dict):
ce = CustomErrorContract(self.compilation_unit)
ce.set_contract(self._contract)
ce.set_offset(custom_error["src"], self.compilation_unit)
ce_parser = CustomErrorSolc(ce, custom_error, self._slither_parser)
self._contract.custom_errors_as_dict[ce.name] = ce
self._custom_errors_parser.append(ce_parser)
def parse_custom_errors(self):
for father in self._contract.inheritance_reverse:
self._contract.custom_errors_as_dict.update(father.custom_errors_as_dict)
for custom_error in self._customErrorParsed:
self._parse_custom_error(custom_error)
self._customErrorParsed = None
def parse_state_variables(self):
for father in self._contract.inheritance_reverse:
self._contract.variables_as_dict.update(father.variables_as_dict)
@ -600,6 +623,10 @@ class ContractSolc:
except (VariableNotFound, KeyError) as e:
self.log_incorrect_parsing(f"Missing struct {e}")
def analyze_custom_errors(self):
for custom_error in self._custom_errors_parser:
custom_error.analyze_params()
def analyze_events(self):
try:
for father in self._contract.inheritance_reverse:
@ -640,6 +667,7 @@ class ContractSolc:
self._enumsNotParsed = []
self._structuresNotParsed = []
self._usingForNotParsed = []
self._customErrorParsed = []
# endregion
###################################################################################

@ -0,0 +1,101 @@
from typing import TYPE_CHECKING, Dict
from slither.core.declarations.custom_error import CustomError
from slither.core.variables.local_variable import LocalVariable
from slither.solc_parsing.variables.local_variable import LocalVariableSolc
if TYPE_CHECKING:
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
# Part of the code was copied from the function parsing
# In the long term we should refactor these two classes to merge the duplicated code
class CustomErrorSolc:
def __init__(
self,
custom_error: CustomError,
custom_error_data: dict,
slither_parser: "SlitherCompilationUnitSolc",
):
self._slither_parser: "SlitherCompilationUnitSolc" = slither_parser
self._custom_error = custom_error
custom_error.name = custom_error_data["name"]
self._params_was_analyzed = False
if not self._slither_parser.is_compact_ast:
custom_error_data = custom_error_data["attributes"]
self._custom_error_data = custom_error_data
def analyze_params(self):
# Can be re-analyzed due to inheritance
if self._params_was_analyzed:
return
self._params_was_analyzed = True
if self._slither_parser.is_compact_ast:
params = self._custom_error_data["parameters"]
else:
children = self._custom_error_data[self.get_children("children")]
# It uses to be
# params = children[0]
# returns = children[1]
# But from Solidity 0.6.3 to 0.6.10 (included)
# Comment above a function might be added in the children
child_iter = iter(
[child for child in children if child[self.get_key()] == "ParameterList"]
)
params = next(child_iter)
if params:
self._parse_params(params)
@property
def is_compact_ast(self) -> bool:
return self._slither_parser.is_compact_ast
def get_key(self) -> str:
return self._slither_parser.get_key()
def get_children(self, key: str) -> str:
if self._slither_parser.is_compact_ast:
return key
return "children"
def _parse_params(self, params: Dict):
assert params[self.get_key()] == "ParameterList"
if self._slither_parser.is_compact_ast:
params = params["parameters"]
else:
params = params[self.get_children("children")]
for param in params:
assert param[self.get_key()] == "VariableDeclaration"
local_var = self._add_param(param)
self._custom_error.add_parameters(local_var.underlying_variable)
def _add_param(self, param: Dict) -> LocalVariableSolc:
local_var = LocalVariable()
local_var.set_offset(param["src"], self._slither_parser.compilation_unit)
local_var_parser = LocalVariableSolc(local_var, param)
local_var_parser.analyze(self)
# see https://solidity.readthedocs.io/en/v0.4.24/types.html?highlight=storage%20location#data-location
if local_var.location == "default":
local_var.set_location("memory")
return local_var_parser
@property
def underlying_custom_error(self) -> CustomError:
return self._custom_error
@property
def slither_parser(self) -> "SlitherCompilationUnitSolc":
return self._slither_parser

@ -10,12 +10,11 @@ from slither.core.declarations.function import (
FunctionType,
)
from slither.core.declarations.function_contract import FunctionContract
from slither.core.expressions import AssignmentOperation
from slither.core.variables.local_variable import LocalVariable
from slither.core.variables.local_variable_init_from_tuple import LocalVariableInitFromTuple
from slither.solc_parsing.cfg.node import NodeSolc
from slither.solc_parsing.exceptions import ParsingError
from slither.solc_parsing.expressions.expression_parsing import parse_expression
from slither.solc_parsing.variables.local_variable import LocalVariableSolc
from slither.solc_parsing.variables.local_variable_init_from_tuple import (
@ -26,13 +25,11 @@ from slither.solc_parsing.yul.parse_yul import YulBlock
from slither.utils.expression_manipulations import SplitTernaryExpression
from slither.visitors.expression.export_values import ExportValues
from slither.visitors.expression.has_conditional import HasConditional
from slither.solc_parsing.exceptions import ParsingError
if TYPE_CHECKING:
from slither.core.expressions.expression import Expression
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
from slither.core.slither_core import SlitherCore
from slither.core.compilation_unit import SlitherCompilationUnit
@ -1012,6 +1009,15 @@ class FunctionSolc:
node = self._parse_try_catch(statement, node)
# elif name == 'TryCatchClause':
# self._parse_catch(statement, node)
elif name == "RevertStatement":
if self.is_compact_ast:
expression = statement[self.get_children("errorCall")]
else:
expression = statement[self.get_children("errorCall")][0]
new_node = self._new_node(NodeType.EXPRESSION, statement["src"], scope)
new_node.add_unparsed_expression(expression)
link_underlying_nodes(node, new_node)
node = new_node
else:
raise ParsingError("Statement not parsed %s" % name)

@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Optional, Union, List, Tuple
from slither.core.declarations import Event, Enum, Structure
from slither.core.declarations.contract import Contract
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.function import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function_top_level import FunctionTopLevel
@ -100,7 +101,7 @@ def _find_variable_in_function_parser(
def _find_top_level(
var_name: str, sl: "SlitherCompilationUnit"
) -> Tuple[Optional[Union[Enum, Structure, SolidityImportPlaceHolder]], bool]:
) -> Tuple[Optional[Union[Enum, Structure, SolidityImportPlaceHolder, CustomError]], bool]:
"""
Return the top level variable use, and a boolean indicating if the variable returning was cretead
If the variable was created, it has no source_mapping
@ -127,6 +128,11 @@ def _find_top_level(
new_val = SolidityImportPlaceHolder(import_directive)
return new_val, True
# Note for now solidity prevent two custom error from having the same name
for custom_error in sl.custom_errors:
if custom_error.solidity_signature == var_name:
return custom_error, False
return None, False
@ -135,7 +141,7 @@ def _find_in_contract(
contract: Optional[Contract],
contract_declarer: Optional[Contract],
is_super: bool,
) -> Optional[Union[Variable, Function, Contract, Event, Enum, Structure,]]:
) -> Optional[Union[Variable, Function, Contract, Event, Enum, Structure, CustomError]]:
if contract is None or contract_declarer is None:
return None
@ -191,6 +197,14 @@ def _find_in_contract(
if var_name in enums:
return enums[var_name]
# Note: contract.custom_errors_as_dict uses the name (not the sol sig) as key
# This is because when the dic is populated the underlying object is not yet parsed
# As a result, we need to iterate over all the custom errors here instead of using the dict
custom_errors = contract.custom_errors
for custom_error in custom_errors:
if var_name == custom_error.solidity_signature:
return custom_error
# If the enum is refered as its name rather than its canonicalName
enums = {e.name: e for e in contract.enums}
if var_name in enums:
@ -260,6 +274,7 @@ def find_variable(
Event,
Enum,
Structure,
CustomError,
],
bool,
]:
@ -384,4 +399,4 @@ def find_variable(
if ret:
return ret, False
raise VariableNotFound("Variable not found: {} (context {})".format(var_name, caller_context))
raise VariableNotFound("Variable not found: {} (context {})".format(var_name, contract))

@ -8,6 +8,7 @@ from typing import List, Dict
from slither.analyses.data_dependency.data_dependency import compute_dependency
from slither.core.compilation_unit import SlitherCompilationUnit
from slither.core.declarations import Contract
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.import_directive import Import
@ -16,6 +17,7 @@ from slither.core.declarations.structure_top_level import StructureTopLevel
from slither.core.variables.top_level_variable import TopLevelVariable
from slither.exceptions import SlitherException
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.structure_top_level import StructureTopLevelSolc
from slither.solc_parsing.exceptions import VariableNotFound
@ -37,6 +39,7 @@ class SlitherCompilationUnitSolc:
self._underlying_contract_to_parser: Dict[Contract, ContractSolc] = dict()
self._structures_top_level_parser: List[StructureTopLevelSolc] = []
self._custom_error_parser: List[CustomErrorSolc] = []
self._variables_top_level_parser: List[TopLevelVariableSolc] = []
self._functions_top_level_parser: List[FunctionSolc] = []
@ -146,7 +149,7 @@ class SlitherCompilationUnitSolc:
def parse_top_level_from_loaded_json(
self, data_loaded: Dict, filename: str
): # pylint: disable=too-many-branches,too-many-statements
): # pylint: disable=too-many-branches,too-many-statements,too-many-locals
if "nodeType" in data_loaded:
self._is_compact_ast = True
@ -164,6 +167,8 @@ class SlitherCompilationUnitSolc:
logger.error("solc version is not supported")
return
if self.get_children() not in data_loaded:
return
for top_level_data in data_loaded[self.get_children()]:
if top_level_data[self.get_key()] == "ContractDefinition":
contract = Contract(self._compilation_unit)
@ -235,6 +240,14 @@ class SlitherCompilationUnitSolc:
self._functions_top_level_parser.append(func_parser)
self.add_function_or_modifier_parser(func_parser)
elif top_level_data[self.get_key()] == "ErrorDefinition":
custom_error = CustomErrorTopLevel(self._compilation_unit)
custom_error.set_offset(top_level_data["src"], self._compilation_unit)
custom_error_parser = CustomErrorSolc(custom_error, top_level_data, self)
self._compilation_unit.custom_errors.append(custom_error)
self._custom_error_parser.append(custom_error_parser)
else:
raise SlitherException(f"Top level {top_level_data[self.get_key()]} not supported")
@ -522,6 +535,7 @@ Please rename it, this name is reserved for Slither's internals"""
contract.parse_state_variables()
contract.parse_modifiers()
contract.parse_functions()
contract.parse_custom_errors()
contract.set_is_analyzed(True)
def _analyze_struct_events(self, contract: ContractSolc):
@ -534,6 +548,7 @@ Please rename it, this name is reserved for Slither's internals"""
contract.analyze_events()
contract.analyze_using_for()
contract.analyze_custom_errors()
contract.set_is_analyzed(True)
@ -556,6 +571,10 @@ Please rename it, this name is reserved for Slither's internals"""
func_parser.analyze_params()
self._compilation_unit.add_function(func_parser.underlying_function)
def _analyze_params_custom_error(self):
for custom_error_parser in self._custom_error_parser:
custom_error_parser.analyze_params()
def _analyze_content_top_level_function(self):
try:
for func_parser in self._functions_top_level_parser:
@ -569,6 +588,7 @@ Please rename it, this name is reserved for Slither's internals"""
contract.analyze_params_modifiers()
contract.analyze_params_functions()
self._analyze_params_top_level_function()
self._analyze_params_custom_error()
contract.analyze_state_variables()

@ -190,7 +190,12 @@ def _find_from_type_name( # pylint: disable=too-many-locals,too-many-branches,t
return UserDefinedType(var_type)
def parse_type(t: Union[Dict, UnknownType], caller_context):
def parse_type(
t: Union[Dict, UnknownType],
caller_context: Union[
"SlitherCompilationUnitSolc", "FunctionSolc", "ContractSolc", "CustomSolc"
],
):
# local import to avoid circular dependency
# pylint: disable=too-many-locals,too-many-branches,too-many-statements
# pylint: disable=import-outside-toplevel
@ -198,17 +203,22 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
from slither.solc_parsing.variables.function_type_variable import FunctionTypeVariableSolc
from slither.solc_parsing.declarations.contract import ContractSolc
from slither.solc_parsing.declarations.function import FunctionSolc
from slither.solc_parsing.declarations.custom_error import CustomErrorSolc
from slither.solc_parsing.slither_compilation_unit_solc import SlitherCompilationUnitSolc
sl: "SlitherCompilationUnit"
# Note: for convenicence top level functions use the same parser than function in contract
# but contract_parser is set to None
if isinstance(caller_context, SlitherCompilationUnitSolc) or (
if isinstance(caller_context, (SlitherCompilationUnitSolc, CustomErrorSolc)) or (
isinstance(caller_context, FunctionSolc) and caller_context.contract_parser is None
):
structures_direct_access: List["Structure"]
if isinstance(caller_context, SlitherCompilationUnitSolc):
sl = caller_context.compilation_unit
next_context = caller_context
elif isinstance(caller_context, CustomErrorSolc):
sl = caller_context.underlying_custom_error.compilation_unit
next_context = caller_context.slither_parser
else:
assert isinstance(caller_context, FunctionSolc)
sl = caller_context.underlying_function.compilation_unit
@ -235,13 +245,13 @@ def parse_type(t: Union[Dict, UnknownType], caller_context):
contract = caller_context.underlying_contract
next_context = caller_context
structures_direct_access = (
contract.structures + contract.compilation_unit.structures_top_level
)
structures_direct_access = contract.structures
structures_direct_access += contract.compilation_unit.structures_top_level
all_structuress = [c.structures for c in contract.compilation_unit.contracts]
all_structures = [item for sublist in all_structuress for item in sublist]
all_structures += contract.compilation_unit.structures_top_level
enums_direct_access = contract.enums + contract.compilation_unit.enums_top_level
enums_direct_access: List["Enum"] = contract.enums
enums_direct_access += contract.compilation_unit.enums_top_level
all_enumss = [c.enums for c in contract.compilation_unit.contracts]
all_enums = [item for sublist in all_enumss for item in sublist]
all_enums += contract.compilation_unit.enums_top_level

@ -10,7 +10,9 @@ from slither.core.declarations import (
SolidityFunction,
Contract,
)
from slither.core.declarations.function import FunctionLanguage
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.expressions import (
Literal,
AssignmentOperation,
@ -353,7 +355,13 @@ def convert_yul_block(
def convert_yul_function_definition(
root: YulScope, parent: YulNode, ast: Dict, node_scope: Union[Function, Scope]
) -> YulNode:
func = FunctionContract(root.compilation_unit)
top_node_scope = node_scope
while not isinstance(top_node_scope, Function):
top_node_scope = top_node_scope.father
assert isinstance(top_node_scope, (FunctionTopLevel, FunctionContract))
func = type(top_node_scope)(root.compilation_unit)
func.function_language = FunctionLanguage.Yul
yul_function = YulFunction(func, root, ast, node_scope)
root.contract.add_function(func)
@ -707,6 +715,15 @@ def parse_yul_function_call(root: YulScope, node: YulNode, ast: Dict) -> Optiona
raise SlitherException(f"unexpected function call target type {str(type(ident.value))}")
def _check_for_state_variable_name(root: YulScope, potential_name: str) -> Optional[Identifier]:
root_function = root.function
if isinstance(root_function, FunctionContract):
var = root_function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
return None
def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[Expression]:
name = ast["name"]
@ -739,17 +756,17 @@ def parse_yul_identifier(root: YulScope, _node: YulNode, ast: Dict) -> Optional[
# check for magic suffixes
if name.endswith("_slot") or name.endswith(".slot"):
potential_name = name[:-5]
var = root.function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
var = root.function.get_local_variable_from_name(potential_name)
if var and var.is_storage:
return Identifier(var)
if name.endswith("_offset") or name.endswith(".offset"):
potential_name = name[:-7]
var = root.function.contract.get_state_variable_from_name(potential_name)
if var:
return Identifier(var)
variable_found = _check_for_state_variable_name(root, potential_name)
if variable_found:
return variable_found
raise SlitherException(f"unresolved reference to identifier {name}")

@ -88,7 +88,7 @@ class Flattening:
:return:
"""
src_mapping = contract.source_mapping
content = self._slither.source_code[src_mapping["filename_absolute"]].encode("utf8")
content = self._slither.source_code[src_mapping["filename_absolute"]]
start = src_mapping["start"]
end = src_mapping["start"] + src_mapping["length"]
@ -171,7 +171,7 @@ class Flattening:
index = index - start
if patch_type == "public_to_external":
content = content[:index] + "public" + content[index + len("external") :]
if patch_type == "private_to_internal":
elif patch_type == "private_to_internal":
content = content[:index] + "internal" + content[index + len("private") :]
elif patch_type == "calldata_to_memory":
content = content[:index] + "memory" + content[index + len("calldata") :]
@ -179,7 +179,7 @@ class Flattening:
assert patch_type == "line_removal"
content = content[:index] + " // " + content[index:]
self._source_codes[contract] = content.decode("utf8")
self._source_codes[contract] = content
def _pragmas(self) -> str:
"""

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save