very simplistic support for reentrancy lock

pull/2099/head
alpharush 1 year ago
parent 2dbb912bb7
commit f7ef48401e
  1. 4
      slither/core/declarations/function.py
  2. 72
      slither/vyper_parsing/declarations/function.py
  3. 4
      tests/unit/core/test_function_declaration.py

@ -1499,7 +1499,9 @@ class Function(SourceMapping, metaclass=ABCMeta): # pylint: disable=too-many-pu
Determine if the function can be re-entered
"""
# TODO: compare with hash of known nonReentrant modifier instead of the name
if "nonReentrant" in [m.name for m in self.modifiers]:
if "nonReentrant" in [m.name for m in self.modifiers] or "nonreentrant(lock)" in [
m.name for m in self.modifiers
]:
return False
if self.visibility in ["public", "external"]:

@ -8,7 +8,8 @@ from slither.core.declarations.function import (
Function,
FunctionType,
)
from slither.core.declarations.function_contract import FunctionContract
from slither.core.declarations.function import ModifierStatements
from slither.core.declarations.modifier import Modifier
from slither.core.expressions import AssignmentOperation
from slither.core.source_mapping.source_mapping import Source
from slither.core.variables.local_variable import LocalVariable
@ -33,48 +34,47 @@ class FunctionVyper:
function_data: Dict,
contract_parser: "ContractVyper",
) -> None:
self._node_to_NodeVyper: Dict[Node, NodeVyper] = {}
self._function = function
print(function_data.name)
print(function_data)
self._function.name = function_data.name
self._function.id = function_data.node_id
self._functionNotParsed = function_data
self._decoratorNotParsed = None
self._local_variables_parser: List[LocalVariableVyper] = []
self._contract_parser = contract_parser
self._node_to_NodeVyper: Dict[Node, NodeVyper] = {}
for decorator in function_data.decorators:
if not hasattr(decorator, "id"):
continue # TODO isinstance Name
if decorator.id in ["external", "public", "internal"]:
self._function.visibility = decorator.id
elif decorator.id == "view":
self._function.view = True
elif decorator.id == "pure":
self._function.pure = True
elif decorator.id == "payable":
self._function.payable = True
elif decorator.id == "nonpayable":
self._function.payable = False
if isinstance(decorator, Call):
# TODO handle multiple
self._decoratorNotParsed = decorator
elif isinstance(decorator, Name):
if decorator.id in ["external", "public", "internal"]:
self._function.visibility = decorator.id
elif decorator.id == "view":
self._function.view = True
elif decorator.id == "pure":
self._function.pure = True
elif decorator.id == "payable":
self._function.payable = True
elif decorator.id == "nonpayable":
self._function.payable = False
else:
raise ValueError(f"Unknown decorator {decorator.id}")
# Interfaces do not have decorators and are external
if self._function._visibility is None:
self._function.visibility = "external"
self._functionNotParsed = function_data
self._params_was_analyzed = False
self._content_was_analyzed = False
self._counter_scope_local_variables = 0
self._analyze_function_type()
if function_data.doc_string is not None:
function.has_documentation = True
self._analyze_function_type()
@property
def underlying_function(self) -> Function:
return self._function
@ -166,6 +166,34 @@ class FunctionVyper:
for node_parser in self._node_to_NodeVyper.values():
node_parser.analyze_expressions(self._function)
self._analyze_decorator()
def _analyze_decorator(self) -> None:
if not self._decoratorNotParsed:
return
decorator = self._decoratorNotParsed
if decorator.args:
name = f"{decorator.func.id}({decorator.args[0].value})"
else:
name = decorator.func.id
contract = self._contract_parser.underlying_contract
compilation_unit = self._contract_parser.underlying_contract.compilation_unit
modifier = Modifier(compilation_unit)
modifier._name = name
modifier.set_offset(decorator.src, compilation_unit)
modifier.set_contract(contract)
modifier.set_contract_declarer(contract)
latest_entry_point = self._function.entry_point
self._function.add_modifier(
ModifierStatements(
modifier=modifier,
entry_point=latest_entry_point,
nodes=[latest_entry_point],
)
)
# endregion
###################################################################################
###################################################################################

@ -368,8 +368,8 @@ def __default__():
assert f.can_send_eth()
assert f.can_reenter()
# f = functions["withdraw_locked()"]
# assert not f.can_reenter()
f = functions["withdraw_locked()"]
assert not f.is_reentrant
var = contract.get_state_variable_from_name("balances")
assert var

Loading…
Cancel
Save