Skip to content

Commit 00bd1d4

Browse files
committed
Merge branch 'dev' of https://github.com/dokzai/slither into issue-1654
2 parents e11580f + 9e003ee commit 00bd1d4

File tree

136 files changed

+5653
-173
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+5653
-173
lines changed

.github/workflows/test.yml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,23 @@ jobs:
5757
npm install hardhat
5858
popd || exit
5959
fi
60-
60+
- name: Install Vyper
61+
run: |
62+
INSTALLDIR="$RUNNER_TEMP/vyper-install"
63+
if [[ "$RUNNER_OS" = "Windows" ]]; then
64+
URL="https://github.com/vyperlang/vyper/releases/download/v0.3.7/vyper.0.3.7+commit.6020b8bb.windows.exe"
65+
FILENAME="vyper.exe"
66+
elif [[ "$RUNNER_OS" = "Linux" ]]; then
67+
URL="https://github.com/vyperlang/vyper/releases/download/v0.3.7/vyper.0.3.7+commit.6020b8bb.linux"
68+
FILENAME="vyper"
69+
else
70+
echo "Unknown OS"
71+
exit 1
72+
fi
73+
mkdir -p "$INSTALLDIR"
74+
curl "$URL" -o "$INSTALLDIR/$FILENAME" -L
75+
chmod 755 "$INSTALLDIR/$FILENAME"
76+
echo "$INSTALLDIR" >> "$GITHUB_PATH"
6177
- name: Run ${{ matrix.type }} tests
6278
env:
6379
TEST_TYPE: ${{ matrix.type }}

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
"packaging",
1616
"prettytable>=3.3.0",
1717
"pycryptodome>=3.4.6",
18-
"crytic-compile>=0.3.3,<0.4.0",
19-
# "crytic-compile@git+https://github.com/crytic/crytic-compile.git@dev#egg=crytic-compile",
18+
# "crytic-compile>=0.3.1,<0.4.0",
19+
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
2020
"web3>=6.0.0",
2121
"eth-abi>=4.0.0",
2222
"eth-typing>=3.0.0",

slither/__main__.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -870,12 +870,6 @@ def main_impl(
870870
logging.error(red(output_error))
871871
logging.error("Please report an issue to https://github.com/crytic/slither/issues")
872872

873-
except Exception: # pylint: disable=broad-except
874-
output_error = traceback.format_exc()
875-
traceback.print_exc()
876-
logging.error(f"Error in {args.filename}") # pylint: disable=logging-fstring-interpolation
877-
logging.error(output_error)
878-
879873
# If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON.
880874
if outputting_json:
881875
if "console" in args.json_types:

slither/core/compilation_unit.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import math
2+
from enum import Enum
23
from typing import Optional, Dict, List, Set, Union, TYPE_CHECKING, Tuple
34

45
from crytic_compile import CompilationUnit, CryticCompile
@@ -29,13 +30,28 @@
2930
from slither.core.slither_core import SlitherCore
3031

3132

33+
class Language(Enum):
34+
SOLIDITY = "solidity"
35+
VYPER = "vyper"
36+
37+
@staticmethod
38+
def from_str(label: str):
39+
if label == "solc":
40+
return Language.SOLIDITY
41+
if label == "vyper":
42+
return Language.VYPER
43+
44+
raise ValueError(f"Unknown language: {label}")
45+
46+
3247
# pylint: disable=too-many-instance-attributes,too-many-public-methods
3348
class SlitherCompilationUnit(Context):
3449
def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit) -> None:
3550
super().__init__()
3651

3752
self._core = core
3853
self._crytic_compile_compilation_unit = crytic_compilation_unit
54+
self._language = Language.from_str(crytic_compilation_unit.compiler_version.compiler)
3955

4056
# Top level object
4157
self.contracts: List[Contract] = []
@@ -81,6 +97,17 @@ def source_units(self) -> Dict[int, str]:
8197
# region Compiler
8298
###################################################################################
8399
###################################################################################
100+
@property
101+
def language(self) -> Language:
102+
return self._language
103+
104+
@property
105+
def is_vyper(self) -> bool:
106+
return self._language == Language.VYPER
107+
108+
@property
109+
def is_solidity(self) -> bool:
110+
return self._language == Language.SOLIDITY
84111

85112
@property
86113
def compiler_version(self) -> CompilerVersion:
@@ -166,6 +193,10 @@ def functions_and_modifiers(self) -> List[Function]:
166193
return self.functions + list(self.modifiers)
167194

168195
def propagate_function_calls(self) -> None:
196+
"""This info is used to compute the rvalues of Phi operations in `fix_phi` and ultimately
197+
is responsible for the `read` property of Phi operations which is vital to
198+
propagating taints inter-procedurally
199+
"""
169200
for f in self.functions_and_modifiers:
170201
for node in f.nodes:
171202
for ir in node.irs_ssa:
@@ -259,6 +290,7 @@ def get_scope(self, filename_str: str) -> FileScope:
259290
###################################################################################
260291

261292
def compute_storage_layout(self) -> None:
293+
assert self.is_solidity
262294
for contract in self.contracts_derived:
263295
self._storage_layouts[contract.name] = {}
264296

slither/core/declarations/contract.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ def name(self, name: str) -> None:
138138
@property
139139
def id(self) -> int:
140140
"""Unique id."""
141-
assert self._id
141+
assert self._id is not None
142142
return self._id
143143

144144
@id.setter

slither/core/declarations/function.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
137137
self._parameters: List["LocalVariable"] = []
138138
self._parameters_ssa: List["LocalIRVariable"] = []
139139
self._parameters_src: SourceMapping = SourceMapping()
140+
# This is used for vyper calls with default arguments
141+
self._default_args_as_expressions: List["Expression"] = []
140142
self._returns: List["LocalVariable"] = []
141143
self._returns_ssa: List["LocalIRVariable"] = []
142144
self._returns_src: SourceMapping = SourceMapping()
@@ -217,8 +219,9 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
217219

218220
self.compilation_unit: "SlitherCompilationUnit" = compilation_unit
219221

220-
# Assume we are analyzing Solidity by default
221-
self.function_language: FunctionLanguage = FunctionLanguage.Solidity
222+
self.function_language: FunctionLanguage = (
223+
FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
224+
)
222225

223226
self._id: Optional[str] = None
224227

@@ -238,7 +241,7 @@ def name(self) -> str:
238241
"""
239242
if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
240243
return "constructor"
241-
if self._function_type == FunctionType.FALLBACK:
244+
if self._name == "" and self._function_type == FunctionType.FALLBACK:
242245
return "fallback"
243246
if self._function_type == FunctionType.RECEIVE:
244247
return "receive"
@@ -985,14 +988,15 @@ def signature(self) -> Tuple[str, List[str], List[str]]:
985988
(str, list(str), list(str)): Function signature as
986989
(name, list parameters type, list return values type)
987990
"""
988-
if self._signature is None:
989-
signature = (
990-
self.name,
991-
[str(x.type) for x in self.parameters],
992-
[str(x.type) for x in self.returns],
993-
)
994-
self._signature = signature
995-
return self._signature
991+
# FIXME memoizing this function is not working properly for vyper
992+
# if self._signature is None:
993+
return (
994+
self.name,
995+
[str(x.type) for x in self.parameters],
996+
[str(x.type) for x in self.returns],
997+
)
998+
# self._signature = signature
999+
# return self._signature
9961000

9971001
@property
9981002
def signature_str(self) -> str:
@@ -1497,7 +1501,9 @@ def is_reentrant(self) -> bool:
14971501
Determine if the function can be re-entered
14981502
"""
14991503
# TODO: compare with hash of known nonReentrant modifier instead of the name
1500-
if "nonReentrant" in [m.name for m in self.modifiers]:
1504+
if "nonReentrant" in [m.name for m in self.modifiers] or "nonreentrant(lock)" in [
1505+
m.name for m in self.modifiers
1506+
]:
15011507
return False
15021508

15031509
if self.visibility in ["public", "external"]:
@@ -1756,6 +1762,7 @@ def fix_phi(
17561762
node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]
17571763

17581764
def generate_slithir_and_analyze(self) -> None:
1765+
17591766
for node in self.nodes:
17601767
node.slithir_generation()
17611768

slither/core/declarations/solidity_variables.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
SOLIDITY_VARIABLES = {
1111
"now": "uint256",
1212
"this": "address",
13+
"self": "address",
1314
"abi": "address", # to simplify the conversion, assume that abi return an address
1415
"msg": "",
1516
"tx": "",
1617
"block": "",
1718
"super": "",
19+
"chain": "",
20+
"ZERO_ADDRESS": "address",
1821
}
1922

2023
SOLIDITY_VARIABLES_COMPOSED = {
@@ -34,6 +37,10 @@
3437
"msg.value": "uint256",
3538
"tx.gasprice": "uint256",
3639
"tx.origin": "address",
40+
# Vyper
41+
"chain.id": "uint256",
42+
"block.prevhash": "bytes32",
43+
"self.balance": "uint256",
3744
}
3845

3946
SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
@@ -81,6 +88,32 @@
8188
"balance(address)": ["uint256"],
8289
"code(address)": ["bytes"],
8390
"codehash(address)": ["bytes32"],
91+
# Vyper
92+
"create_from_blueprint()": [],
93+
"create_minimal_proxy_to()": [],
94+
"empty()": [],
95+
"convert()": [],
96+
"len()": ["uint256"],
97+
"method_id()": [],
98+
"unsafe_sub()": [],
99+
"unsafe_add()": [],
100+
"unsafe_div()": [],
101+
"unsafe_mul()": [],
102+
"pow_mod256()": [],
103+
"max_value()": [],
104+
"min_value()": [],
105+
"concat()": [],
106+
"ecrecover()": [],
107+
"isqrt()": [],
108+
"range()": [],
109+
"min()": [],
110+
"max()": [],
111+
"shift()": [],
112+
"abs()": [],
113+
"raw_call()": ["bool", "bytes32"],
114+
"_abi_encode()": [],
115+
"slice()": [],
116+
"uint2str()": ["string"],
84117
}
85118

86119

slither/core/expressions/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .new_elementary_type import NewElementaryType
1313
from .super_call_expression import SuperCallExpression
1414
from .super_identifier import SuperIdentifier
15+
from .self_identifier import SelfIdentifier
1516
from .tuple_expression import TupleExpression
1617
from .type_conversion import TypeConversion
1718
from .unary_operation import UnaryOperation, UnaryOperationType

slither/core/expressions/binary_operation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class BinaryOperationType(Enum):
4242
# pylint: disable=too-many-branches
4343
@staticmethod
4444
def get_type(
45-
operation_type: "BinaryOperation",
45+
operation_type: "str",
4646
) -> "BinaryOperationType":
4747
if operation_type == "**":
4848
return BinaryOperationType.POWER

slither/core/expressions/identifier.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ def __init__(
2626
],
2727
) -> None:
2828
super().__init__()
29-
3029
# pylint: disable=import-outside-toplevel
3130
from slither.core.declarations import Contract, SolidityVariable, SolidityFunction
3231
from slither.solc_parsing.yul.evm_functions import YulBuiltin
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from slither.core.expressions.identifier import Identifier
2+
3+
4+
class SelfIdentifier(Identifier):
5+
def __str__(self):
6+
return "self." + str(self._value)

slither/core/variables/variable.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,5 +179,6 @@ def solidity_signature(self) -> str:
179179
return f'{name}({",".join(parameters)})'
180180

181181
def __str__(self) -> str:
182-
assert self._name
182+
if self._name is None:
183+
return ""
183184
return self._name

slither/detectors/abstract_detector.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from logging import Logger
44
from typing import Optional, List, TYPE_CHECKING, Dict, Union, Callable
55

6-
from slither.core.compilation_unit import SlitherCompilationUnit
6+
from slither.core.compilation_unit import SlitherCompilationUnit, Language
77
from slither.core.declarations import Contract
88
from slither.formatters.exceptions import FormatImpossible
99
from slither.formatters.utils.patches import apply_patch, create_diff
@@ -80,6 +80,9 @@ class AbstractDetector(metaclass=abc.ABCMeta):
8080
# list of vulnerable solc versions as strings (e.g. ["0.4.25", "0.5.0"])
8181
# If the detector is meant to run on all versions, use None
8282
VULNERABLE_SOLC_VERSIONS: Optional[List[str]] = None
83+
# If the detector is meant to run on all languages, use None
84+
# Otherwise, use `solidity` or `vyper`
85+
LANGUAGE: Optional[str] = None
8386

8487
def __init__(
8588
self, compilation_unit: SlitherCompilationUnit, slither: "Slither", logger: Logger
@@ -133,6 +136,14 @@ def __init__(
133136
f"VULNERABLE_SOLC_VERSIONS should not be an empty list {self.__class__.__name__}"
134137
)
135138

139+
if self.LANGUAGE is not None and self.LANGUAGE not in [
140+
Language.SOLIDITY.value,
141+
Language.VYPER.value,
142+
]:
143+
raise IncorrectDetectorInitialization(
144+
f"LANGUAGE should not be either 'solidity' or 'vyper' {self.__class__.__name__}"
145+
)
146+
136147
if re.match("^[a-zA-Z0-9_-]*$", self.ARGUMENT) is None:
137148
raise IncorrectDetectorInitialization(
138149
f"ARGUMENT has illegal character {self.__class__.__name__}"
@@ -164,9 +175,14 @@ def _log(self, info: str) -> None:
164175
if self.logger:
165176
self.logger.info(self.color(info))
166177

167-
def _uses_vulnerable_solc_version(self) -> bool:
178+
def _is_applicable_detector(self) -> bool:
168179
if self.VULNERABLE_SOLC_VERSIONS:
169-
return self.compilation_unit.solc_version in self.VULNERABLE_SOLC_VERSIONS
180+
return (
181+
self.compilation_unit.is_solidity
182+
and self.compilation_unit.solc_version in self.VULNERABLE_SOLC_VERSIONS
183+
)
184+
if self.LANGUAGE:
185+
return self.compilation_unit.language.value == self.LANGUAGE
170186
return True
171187

172188
@abc.abstractmethod
@@ -179,7 +195,7 @@ def detect(self) -> List[Dict]:
179195
results: List[Dict] = []
180196

181197
# check solc version
182-
if not self._uses_vulnerable_solc_version():
198+
if not self._is_applicable_detector():
183199
return results
184200

185201
# only keep valid result, and remove duplicate

slither/detectors/all_detectors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,8 @@
9292
from .operations.cache_array_length import CacheArrayLength
9393
from .statements.incorrect_using_for import IncorrectUsingFor
9494
from .operations.encode_packed import EncodePackedCollision
95+
from .assembly.incorrect_return import IncorrectReturn
96+
from .assembly.return_instead_of_leave import ReturnInsteadOfLeave
97+
from .operations.incorrect_exp import IncorrectOperatorExponentiation
98+
from .statements.tautological_compare import TautologicalCompare
99+
from .statements.return_bomb import ReturnBomb

0 commit comments

Comments
 (0)