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

Feat/vyper support #2099

Merged
merged 71 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
7b3e064
WIP
0xalpharush Aug 18, 2023
b264512
improve parse_types
0xalpharush Aug 18, 2023
90dc42f
fix interface referring to constants, plus comments
0xalpharush Aug 20, 2023
b142d23
check in progress on comparison operators and builtins
0xalpharush Aug 22, 2023
6fd82fe
aug assign op
0xalpharush Aug 23, 2023
75c3389
wip rewrite of for loops
0xalpharush Aug 24, 2023
2f94d1c
more builtins; support params, state vars in loop iterators
0xalpharush Aug 24, 2023
872d9b5
support enum bitwise comparison operations
0xalpharush Aug 24, 2023
7549341
add some builtin variables for vyper
0xalpharush Aug 28, 2023
fd7c130
allow type conversion to array type for vyper
0xalpharush Aug 28, 2023
a8641d2
add anon. local for return signature
0xalpharush Aug 28, 2023
4c78fe0
support conversion of interface to address, lookup interface funcs
0xalpharush Aug 28, 2023
cff917f
handle stldib interfaces
0xalpharush Aug 29, 2023
3e461ca
initialized vars with expr
0xalpharush Aug 29, 2023
71894ba
perform ssa conversion and analysis
0xalpharush Aug 29, 2023
b1cb181
remove unused code
0xalpharush Aug 29, 2023
b537b70
fix type
0xalpharush Aug 29, 2023
8a3fb6a
fix div symbol
0xalpharush Aug 29, 2023
472efb9
improve comparison operator support, set_offset of expressions
0xalpharush Aug 29, 2023
011c03a
control flow graph (loops and ifs)
0xalpharush Aug 29, 2023
6bd52fa
more builtins
0xalpharush Aug 29, 2023
5443132
fix source mapping, set source code and source unit
0xalpharush Aug 29, 2023
a61ca34
parse bool op symbol
0xalpharush Aug 29, 2023
4b07cbb
add support for self.balance
0xalpharush Aug 30, 2023
db88cd5
fmt
0xalpharush Aug 30, 2023
85e63c6
rename fixture
0xalpharush Aug 30, 2023
55ab580
convert raw_call to LowLevelCall
0xalpharush Aug 30, 2023
4c1ad51
handle edge case for vyper Expr nodes
0xalpharush Aug 30, 2023
2bfe829
add ability to perform filtering by language to AbstractDetector
0xalpharush Aug 30, 2023
77637d9
delete unused code, misc. notes
0xalpharush Aug 30, 2023
3725792
delete more unused code
0xalpharush Aug 30, 2023
917c5d6
add IncorrectSolc detector to solidity-only detectors
0xalpharush Aug 30, 2023
5197b15
fix filtering to use string value of enum
0xalpharush Aug 30, 2023
baa3c98
install vyper in CI
0xalpharush Aug 30, 2023
ae45f46
initial tests
0xalpharush Aug 30, 2023
b5778ce
use curl to support windows
0xalpharush Aug 30, 2023
0fd1ed7
follow redirects
0xalpharush Aug 30, 2023
ce6d3c9
point at vyper crytic-compile branch
0xalpharush Aug 30, 2023
929c2af
add deprecated calls to solidity only detectors
0xalpharush Aug 30, 2023
eadcd46
remove solc imports from vyper parsing
0xalpharush Aug 30, 2023
d368676
fix missing call to set_offset
0xalpharush Aug 31, 2023
36c60a9
add failing phi test
0xalpharush Aug 31, 2023
0efef8b
add test for parsing and cfgir
0xalpharush Aug 31, 2023
08af1ee
cleanup FunctionVyper
0xalpharush Aug 31, 2023
f9633ca
fix name resolution for shadowed state variable
0xalpharush Aug 31, 2023
2dbb912
correctly set indexed attribute for event variables
0xalpharush Aug 31, 2023
f7ef484
very simplistic support for reentrancy lock
0xalpharush Aug 31, 2023
4b10a0f
delete commented out code
0xalpharush Aug 31, 2023
e561d33
try removing trailing comment
0xalpharush Aug 31, 2023
3ca9d0f
convert tuple on lhs and index access to struct and field access
0xalpharush Sep 2, 2023
47e274c
consider function unimplemented if body is only pass
0xalpharush Sep 2, 2023
66def31
do not considered local variable that are reference types as `in_stor…
0xalpharush Sep 2, 2023
e287b2f
fix phi placement by calling SlitherCompilationUnit.add_function
0xalpharush Sep 2, 2023
81cb124
support default args in calls
0xalpharush Sep 4, 2023
4845a3b
fix and refactor comparison operator, add support for raise
0xalpharush Sep 4, 2023
9fdc7dc
conditionally apply refinements to Solidity
0xalpharush Sep 4, 2023
16140f2
cleanup and formatting
0xalpharush Sep 4, 2023
9c4bc50
handle break/continue
0xalpharush Sep 5, 2023
9df54e9
update tests
0xalpharush Sep 5, 2023
a6209df
cleanup
0xalpharush Sep 5, 2023
c217980
fix some lint warnings
0xalpharush Sep 5, 2023
e80238d
add struct defs to file scope
0xalpharush Sep 5, 2023
239369c
propagate return type on call to state variable with interface type
0xalpharush Sep 5, 2023
e8fa8b8
more cleanup
0xalpharush Sep 5, 2023
961db45
address lints and improve typing
0xalpharush Sep 5, 2023
404914c
link issues for TODO comments, lint
0xalpharush Sep 5, 2023
21eca9d
fix merge conflict
0xalpharush Sep 8, 2023
37d714b
Update setup.py
montyly Oct 12, 2023
26cfe00
Merge branch 'dev' into feat/vyper-support
montyly Oct 12, 2023
4a83858
fix CI
montyly Oct 12, 2023
2a7e514
Fix snapshot
montyly Oct 12, 2023
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
18 changes: 17 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,23 @@ jobs:
npm install hardhat
popd || exit
fi

- name: Install Vyper
run: |
INSTALLDIR="$RUNNER_TEMP/vyper-install"
if [[ "$RUNNER_OS" = "Windows" ]]; then
URL="https://github.com/vyperlang/vyper/releases/download/v0.3.7/vyper.0.3.7+commit.6020b8bb.windows.exe"
FILENAME="vyper.exe"
elif [[ "$RUNNER_OS" = "Linux" ]]; then
URL="https://github.com/vyperlang/vyper/releases/download/v0.3.7/vyper.0.3.7+commit.6020b8bb.linux"
FILENAME="vyper"
else
echo "Unknown OS"
exit 1
fi
mkdir -p "$INSTALLDIR"
curl "$URL" -o "$INSTALLDIR/$FILENAME" -L
chmod 755 "$INSTALLDIR/$FILENAME"
echo "$INSTALLDIR" >> "$GITHUB_PATH"
- name: Run ${{ matrix.type }} tests
env:
TEST_TYPE: ${{ matrix.type }}
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"packaging",
"prettytable>=3.3.0",
"pycryptodome>=3.4.6",
"crytic-compile>=0.3.3,<0.4.0",
# "crytic-compile@git+https://github.com/crytic/crytic-compile.git@dev#egg=crytic-compile",
# "crytic-compile>=0.3.1,<0.4.0",
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@master#egg=crytic-compile",
"web3>=6.0.0",
"eth-abi>=4.0.0",
"eth-typing>=3.0.0",
Expand Down
6 changes: 0 additions & 6 deletions slither/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -870,12 +870,6 @@ def main_impl(
logging.error(red(output_error))
logging.error("Please report an issue to https://github.com/crytic/slither/issues")

except Exception: # pylint: disable=broad-except
output_error = traceback.format_exc()
traceback.print_exc()
logging.error(f"Error in {args.filename}") # pylint: disable=logging-fstring-interpolation
logging.error(output_error)

# If we are outputting JSON, capture the redirected output and disable the redirect to output the final JSON.
if outputting_json:
if "console" in args.json_types:
Expand Down
32 changes: 32 additions & 0 deletions slither/core/compilation_unit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import math
from enum import Enum
from typing import Optional, Dict, List, Set, Union, TYPE_CHECKING, Tuple

from crytic_compile import CompilationUnit, CryticCompile
Expand Down Expand Up @@ -29,13 +30,28 @@
from slither.core.slither_core import SlitherCore


class Language(Enum):
SOLIDITY = "solidity"
VYPER = "vyper"

@staticmethod
def from_str(label: str):
if label == "solc":
return Language.SOLIDITY
if label == "vyper":
return Language.VYPER

raise ValueError(f"Unknown language: {label}")


# pylint: disable=too-many-instance-attributes,too-many-public-methods
class SlitherCompilationUnit(Context):
def __init__(self, core: "SlitherCore", crytic_compilation_unit: CompilationUnit) -> None:
super().__init__()

self._core = core
self._crytic_compile_compilation_unit = crytic_compilation_unit
self._language = Language.from_str(crytic_compilation_unit.compiler_version.compiler)

# Top level object
self.contracts: List[Contract] = []
Expand Down Expand Up @@ -81,6 +97,17 @@ def source_units(self) -> Dict[int, str]:
# region Compiler
###################################################################################
###################################################################################
@property
def language(self) -> Language:
return self._language

@property
def is_vyper(self) -> bool:
return self._language == Language.VYPER

@property
def is_solidity(self) -> bool:
return self._language == Language.SOLIDITY

@property
def compiler_version(self) -> CompilerVersion:
Expand Down Expand Up @@ -166,6 +193,10 @@ def functions_and_modifiers(self) -> List[Function]:
return self.functions + list(self.modifiers)

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

def compute_storage_layout(self) -> None:
assert self.is_solidity
for contract in self.contracts_derived:
self._storage_layouts[contract.name] = {}

Expand Down
2 changes: 1 addition & 1 deletion slither/core/declarations/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def name(self, name: str) -> None:
@property
def id(self) -> int:
"""Unique id."""
assert self._id
assert self._id is not None
return self._id

@id.setter
Expand Down
31 changes: 19 additions & 12 deletions slither/core/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:
self._parameters: List["LocalVariable"] = []
self._parameters_ssa: List["LocalIRVariable"] = []
self._parameters_src: SourceMapping = SourceMapping()
# This is used for vyper calls with default arguments
self._default_args_as_expressions: List["Expression"] = []
self._returns: List["LocalVariable"] = []
self._returns_ssa: List["LocalIRVariable"] = []
self._returns_src: SourceMapping = SourceMapping()
Expand Down Expand Up @@ -217,8 +219,9 @@ def __init__(self, compilation_unit: "SlitherCompilationUnit") -> None:

self.compilation_unit: "SlitherCompilationUnit" = compilation_unit

# Assume we are analyzing Solidity by default
self.function_language: FunctionLanguage = FunctionLanguage.Solidity
self.function_language: FunctionLanguage = (
FunctionLanguage.Solidity if compilation_unit.is_solidity else FunctionLanguage.Vyper
)

self._id: Optional[str] = None

Expand All @@ -238,7 +241,7 @@ def name(self) -> str:
"""
if self._name == "" and self._function_type == FunctionType.CONSTRUCTOR:
return "constructor"
if self._function_type == FunctionType.FALLBACK:
if self._name == "" and self._function_type == FunctionType.FALLBACK:
return "fallback"
if self._function_type == FunctionType.RECEIVE:
return "receive"
Expand Down Expand Up @@ -985,14 +988,15 @@ def signature(self) -> Tuple[str, List[str], List[str]]:
(str, list(str), list(str)): Function signature as
(name, list parameters type, list return values type)
"""
if self._signature is None:
signature = (
self.name,
[str(x.type) for x in self.parameters],
[str(x.type) for x in self.returns],
)
self._signature = signature
return self._signature
# FIXME memoizing this function is not working properly for vyper
# if self._signature is None:
return (
self.name,
[str(x.type) for x in self.parameters],
[str(x.type) for x in self.returns],
)
# self._signature = signature
# return self._signature

@property
def signature_str(self) -> str:
Expand Down Expand Up @@ -1497,7 +1501,9 @@ def is_reentrant(self) -> bool:
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"]:
Expand Down Expand Up @@ -1756,6 +1762,7 @@ def fix_phi(
node.irs_ssa = [ir for ir in node.irs_ssa if not self._unchange_phi(ir)]

def generate_slithir_and_analyze(self) -> None:

for node in self.nodes:
node.slithir_generation()

Expand Down
33 changes: 33 additions & 0 deletions slither/core/declarations/solidity_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
SOLIDITY_VARIABLES = {
"now": "uint256",
"this": "address",
"self": "address",
"abi": "address", # to simplify the conversion, assume that abi return an address
"msg": "",
"tx": "",
"block": "",
"super": "",
"chain": "",
"ZERO_ADDRESS": "address",
}

SOLIDITY_VARIABLES_COMPOSED = {
Expand All @@ -34,6 +37,10 @@
"msg.value": "uint256",
"tx.gasprice": "uint256",
"tx.origin": "address",
# Vyper
"chain.id": "uint256",
"block.prevhash": "bytes32",
"self.balance": "uint256",
}

SOLIDITY_FUNCTIONS: Dict[str, List[str]] = {
Expand Down Expand Up @@ -81,6 +88,32 @@
"balance(address)": ["uint256"],
"code(address)": ["bytes"],
"codehash(address)": ["bytes32"],
# Vyper
"create_from_blueprint()": [],
"create_minimal_proxy_to()": [],
"empty()": [],
"convert()": [],
"len()": ["uint256"],
"method_id()": [],
"unsafe_sub()": [],
"unsafe_add()": [],
"unsafe_div()": [],
"unsafe_mul()": [],
"pow_mod256()": [],
"max_value()": [],
"min_value()": [],
"concat()": [],
"ecrecover()": [],
"isqrt()": [],
"range()": [],
"min()": [],
"max()": [],
"shift()": [],
"abs()": [],
"raw_call()": ["bool", "bytes32"],
"_abi_encode()": [],
"slice()": [],
"uint2str()": ["string"],
}


Expand Down
1 change: 1 addition & 0 deletions slither/core/expressions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from .new_elementary_type import NewElementaryType
from .super_call_expression import SuperCallExpression
from .super_identifier import SuperIdentifier
from .self_identifier import SelfIdentifier
from .tuple_expression import TupleExpression
from .type_conversion import TypeConversion
from .unary_operation import UnaryOperation, UnaryOperationType
2 changes: 1 addition & 1 deletion slither/core/expressions/binary_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class BinaryOperationType(Enum):
# pylint: disable=too-many-branches
@staticmethod
def get_type(
operation_type: "BinaryOperation",
operation_type: "str",
) -> "BinaryOperationType":
if operation_type == "**":
return BinaryOperationType.POWER
Expand Down
1 change: 0 additions & 1 deletion slither/core/expressions/identifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def __init__(
],
) -> None:
super().__init__()

# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract, SolidityVariable, SolidityFunction
from slither.solc_parsing.yul.evm_functions import YulBuiltin
Expand Down
6 changes: 6 additions & 0 deletions slither/core/expressions/self_identifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from slither.core.expressions.identifier import Identifier


class SelfIdentifier(Identifier):
def __str__(self):
return "self." + str(self._value)
24 changes: 20 additions & 4 deletions slither/detectors/abstract_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from logging import Logger
from typing import Optional, List, TYPE_CHECKING, Dict, Union, Callable

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

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

if self.LANGUAGE is not None and self.LANGUAGE not in [
Language.SOLIDITY.value,
Language.VYPER.value,
]:
raise IncorrectDetectorInitialization(
f"LANGUAGE should not be either 'solidity' or 'vyper' {self.__class__.__name__}"
)

if re.match("^[a-zA-Z0-9_-]*$", self.ARGUMENT) is None:
raise IncorrectDetectorInitialization(
f"ARGUMENT has illegal character {self.__class__.__name__}"
Expand Down Expand Up @@ -164,9 +175,14 @@ def _log(self, info: str) -> None:
if self.logger:
self.logger.info(self.color(info))

def _uses_vulnerable_solc_version(self) -> bool:
def _is_applicable_detector(self) -> bool:
if self.VULNERABLE_SOLC_VERSIONS:
return self.compilation_unit.solc_version in self.VULNERABLE_SOLC_VERSIONS
return (
self.compilation_unit.is_solidity
and self.compilation_unit.solc_version in self.VULNERABLE_SOLC_VERSIONS
)
if self.LANGUAGE:
return self.compilation_unit.language.value == self.LANGUAGE
return True

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

# check solc version
if not self._uses_vulnerable_solc_version():
if not self._is_applicable_detector():
return results

# only keep valid result, and remove duplicate
Expand Down
2 changes: 1 addition & 1 deletion slither/detectors/attributes/incorrect_solc.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class IncorrectSolc(AbstractDetector):
HELP = "Incorrect Solidity version"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH

LANGUAGE = "solidity"
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-versions-of-solidity"

WIKI_TITLE = "Incorrect versions of Solidity"
Expand Down
2 changes: 1 addition & 1 deletion slither/detectors/naming_convention/naming_convention.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class NamingConvention(AbstractDetector):
HELP = "Conformity to Solidity naming conventions"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH

LANGUAGE = "solidity"
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#conformance-to-solidity-naming-conventions"

WIKI_TITLE = "Conformance to Solidity naming conventions"
Expand Down
2 changes: 1 addition & 1 deletion slither/detectors/statements/deprecated_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class DeprecatedStandards(AbstractDetector):
HELP = "Deprecated Solidity Standards"
IMPACT = DetectorClassification.INFORMATIONAL
CONFIDENCE = DetectorClassification.HIGH

LANGUAGE = "solidity"
WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#deprecated-standards"

WIKI_TITLE = "Deprecated standards"
Expand Down
Loading