diff --git a/slither/__main__.py b/slither/__main__.py index 89678b291..afbdc689c 100644 --- a/slither/__main__.py +++ b/slither/__main__.py @@ -24,6 +24,7 @@ from slither.utils.command_line import (output_detectors, output_results_to_mark output_detectors_json, output_printers, output_to_markdown, output_wiki) from crytic_compile import is_supported +from slither.exceptions import SlitherException logging.basicConfig() logger = logging.getLogger("Slither") @@ -551,6 +552,12 @@ def main_impl(all_detector_classes, all_printer_classes): return exit(results) + except SlitherException as e: + logging.error(red('Error:')) + logging.error(red(e)) + logging.error('Please report an issue to https://github.com/crytic/slither/issues') + sys.exit(-1) + except Exception: logging.error('Error in %s' % args.filename) logging.error(traceback.format_exc()) diff --git a/slither/all_exceptions.py b/slither/all_exceptions.py new file mode 100644 index 000000000..39412971d --- /dev/null +++ b/slither/all_exceptions.py @@ -0,0 +1,7 @@ +""" +This module import all slither exceptions +""" +from slither.slithir.exceptions import SlithIRError +from slither.solc_parsing.exceptions import ParsingError, ParsingContractNotFound, ParsingNameReuse +from slither.core.exceptions import SlitherCoreError +from slither.exceptions import SlitherException \ No newline at end of file diff --git a/slither/core/exceptions.py b/slither/core/exceptions.py new file mode 100644 index 000000000..3f4db4bbf --- /dev/null +++ b/slither/core/exceptions.py @@ -0,0 +1,3 @@ +from slither.exceptions import SlitherException + +class SlitherCoreError(SlitherException): pass diff --git a/slither/core/expressions/assignment_operation.py b/slither/core/expressions/assignment_operation.py index fe7d524bf..749e3843a 100644 --- a/slither/core/expressions/assignment_operation.py +++ b/slither/core/expressions/assignment_operation.py @@ -1,7 +1,7 @@ import logging from slither.core.expressions.expression_typed import ExpressionTyped from slither.core.expressions.expression import Expression - +from slither.core.exceptions import SlitherCoreError logger = logging.getLogger("AssignmentOperation") @@ -43,8 +43,7 @@ class AssignmentOperationType: if operation_type == '%=': return AssignmentOperationType.ASSIGN_MODULO - logger.error('get_type: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlitherCoreError('get_type: Unknown operation type {})'.format(operation_type)) @staticmethod def str(operation_type): @@ -71,8 +70,7 @@ class AssignmentOperationType: if operation_type == AssignmentOperationType.ASSIGN_MODULO: return '%=' - logger.error('str: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlitherCoreError('str: Unknown operation type {})'.format(operation_type)) class AssignmentOperation(ExpressionTyped): diff --git a/slither/core/expressions/binary_operation.py b/slither/core/expressions/binary_operation.py index 9da7cb5df..8ede21931 100644 --- a/slither/core/expressions/binary_operation.py +++ b/slither/core/expressions/binary_operation.py @@ -1,7 +1,7 @@ import logging from slither.core.expressions.expression_typed import ExpressionTyped from slither.core.expressions.expression import Expression - +from slither.core.exceptions import SlitherCoreError logger = logging.getLogger("BinaryOperation") @@ -67,8 +67,7 @@ class BinaryOperationType: if operation_type == '||': return BinaryOperationType.OROR - logger.error('get_type: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlitherCoreError('get_type: Unknown operation type {})'.format(operation_type)) @staticmethod def str(operation_type): @@ -110,8 +109,7 @@ class BinaryOperationType: return '&&' if operation_type == BinaryOperationType.OROR: return '||' - logger.error('str: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlitherCoreError('str: Unknown operation type {})'.format(operation_type)) class BinaryOperation(ExpressionTyped): diff --git a/slither/core/expressions/unary_operation.py b/slither/core/expressions/unary_operation.py index 3416cd854..82cb52eb5 100644 --- a/slither/core/expressions/unary_operation.py +++ b/slither/core/expressions/unary_operation.py @@ -1,7 +1,7 @@ import logging from slither.core.expressions.expression_typed import ExpressionTyped from slither.core.expressions.expression import Expression -from slither.core.solidity_types.type import Type +from slither.core.exceptions import SlitherCoreError logger = logging.getLogger("UnaryOperation") @@ -38,8 +38,7 @@ class UnaryOperationType: return UnaryOperationType.PLUSPLUS_POST if operation_type == '--': return UnaryOperationType.MINUSMINUS_POST - logger.error('get_type: Unknown operation type {}'.format(operation_type)) - exit(-1) + raise SlitherCoreError('get_type: Unknown operation type {}'.format(operation_type)) @staticmethod def str(operation_type): @@ -58,8 +57,7 @@ class UnaryOperationType: if operation_type in [UnaryOperationType.MINUSMINUS_PRE, UnaryOperationType.MINUSMINUS_POST]: return '--' - logger.error('str: Unknown operation type {}'.format(operation_type)) - exit(-1) + raise SlitherCoreError('str: Unknown operation type {}'.format(operation_type)) @staticmethod def is_prefix(operation_type): @@ -74,8 +72,7 @@ class UnaryOperationType: elif operation_type in [UnaryOperationType.PLUSPLUS_POST, UnaryOperationType.MINUSMINUS_POST]: return False - logger.error('is_prefix: Unknown operation type {}'.format(operation_type)) - exit(-1) + raise SlitherCoreError('is_prefix: Unknown operation type {}'.format(operation_type)) class UnaryOperation(ExpressionTyped): diff --git a/slither/exceptions.py b/slither/exceptions.py new file mode 100644 index 000000000..85f7e737b --- /dev/null +++ b/slither/exceptions.py @@ -0,0 +1,3 @@ +class SlitherException(Exception): pass + +class SlitherError(SlitherException): pass diff --git a/slither/slither.py b/slither/slither.py index acb25d619..ccd76ec7c 100644 --- a/slither/slither.py +++ b/slither/slither.py @@ -11,7 +11,7 @@ from crytic_compile import CryticCompile, InvalidCompilation from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification from slither.printers.abstract_printer import AbstractPrinter from .solc_parsing.slitherSolc import SlitherSolc -from .utils.colors import red +from .exceptions import SlitherError logger = logging.getLogger("Slither") logging.basicConfig() @@ -56,11 +56,8 @@ class Slither(SlitherSolc): crytic_compile = CryticCompile(contract, **kwargs) self._crytic_compile = crytic_compile except InvalidCompilation as e: - logger.error('Invalid compilation') - logger.error(e) - exit(-1) + raise SlitherError('Invalid compilation: '+e) for path, ast in crytic_compile.asts.items(): - self._parse_contracts_from_loaded_json(ast, path) self._add_source_code(path) @@ -78,14 +75,12 @@ class Slither(SlitherSolc): def _init_from_raw_json(self, filename): if not os.path.isfile(filename): - logger.error('{} does not exist (are you in the correct directory?)'.format(filename)) - exit(-1) + raise SlitherError('{} does not exist (are you in the correct directory?)'.format(filename)) assert filename.endswith('json') with open(filename, encoding='utf8') as astFile: stdout = astFile.read() if not stdout: - logger.info('Empty AST file: %s', filename) - sys.exit(-1) + raise SlitherError('Empty AST file: %s', filename) contracts_json = stdout.split('\n=') super(Slither, self).__init__(filename) @@ -173,14 +168,12 @@ class Slither(SlitherSolc): def _run_solc(self, filename, solc, disable_solc_warnings, solc_arguments, ast_format): if not os.path.isfile(filename): - logger.error('{} does not exist (are you in the correct directory?)'.format(filename)) - exit(-1) + raise SlitherError('{} does not exist (are you in the correct directory?)'.format(filename)) assert filename.endswith('json') with open(filename, encoding='utf8') as astFile: stdout = astFile.read() if not stdout: - logger.info('Empty AST file: %s', filename) - sys.exit(-1) + raise SlitherError('Empty AST file: %s', filename) stdout = stdout.split('\n=') return stdout diff --git a/slither/slithir/convert.py b/slither/slithir/convert.py index 4534ea607..5208e4641 100644 --- a/slither/slithir/convert.py +++ b/slither/slithir/convert.py @@ -33,6 +33,7 @@ from slither.slithir.variables import (Constant, ReferenceVariable, from slither.visitors.slithir.expression_to_slithir import ExpressionToSlithIR from slither.utils.function import get_function_id from slither.utils.type import export_nested_types_from_variable +from slither.slithir.exceptions import SlithIRError logger = logging.getLogger('ConvertToIR') @@ -457,8 +458,7 @@ def propagate_types(ir, node): # temporary operation; they will be removed pass else: - logger.error('Not handling {} during type propgation'.format(type(ir))) - exit(-1) + raise SlithIRError('Not handling {} during type propgation'.format(type(ir))) def extract_tmp_call(ins, contract): assert isinstance(ins, TmpCall) @@ -577,8 +577,7 @@ def convert_to_low_level(ir): new_ir.arguments = ir.arguments new_ir.lvalue.set_type(ElementaryType('bool')) return new_ir - logger.error('Incorrect conversion to low level {}'.format(ir)) - exit(-1) + raise SlithIRError('Incorrect conversion to low level {}'.format(ir)) def convert_to_push(ir, node): """ diff --git a/slither/slithir/exceptions.py b/slither/slithir/exceptions.py new file mode 100644 index 000000000..b0b82341c --- /dev/null +++ b/slither/slithir/exceptions.py @@ -0,0 +1,3 @@ +from slither.exceptions import SlitherException + +class SlithIRError(SlitherException): pass diff --git a/slither/slithir/operations/binary.py b/slither/slithir/operations/binary.py index 6a4269b5d..e7d004ae4 100644 --- a/slither/slithir/operations/binary.py +++ b/slither/slithir/operations/binary.py @@ -4,6 +4,7 @@ from slither.core.variables.variable import Variable from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue from slither.core.solidity_types import ElementaryType from slither.slithir.variables import ReferenceVariable +from slither.slithir.exceptions import SlithIRError logger = logging.getLogger("BinaryOperationIR") @@ -80,8 +81,7 @@ class BinaryType(object): if operation_type == '||': return BinaryType.OROR - logger.error('get_type: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlithIRError('get_type: Unknown operation type {})'.format(operation_type)) @staticmethod def str(operation_type): @@ -123,8 +123,7 @@ class BinaryType(object): return '&&' if operation_type == BinaryType.OROR: return '||' - logger.error('str: Unknown operation type {})'.format(operation_type)) - exit(-1) + raise SlithIRError('str: Unknown operation type {})'.format(operation_type)) class Binary(OperationWithLValue): diff --git a/slither/slithir/operations/unary.py b/slither/slithir/operations/unary.py index 9a09f09ba..1d3a7726f 100644 --- a/slither/slithir/operations/unary.py +++ b/slither/slithir/operations/unary.py @@ -1,8 +1,7 @@ import logging from slither.slithir.operations.lvalue import OperationWithLValue -from slither.core.variables.variable import Variable - from slither.slithir.utils.utils import is_valid_lvalue, is_valid_rvalue +from slither.slithir.exceptions import SlithIRError logger = logging.getLogger("BinaryOperationIR") @@ -17,8 +16,7 @@ class UnaryType: return UnaryType.BANG if operation_type == '~': return UnaryType.TILD - logger.error('get_type: Unknown operation type {}'.format(operation_type)) - exit(-1) + raise SlithIRError('get_type: Unknown operation type {}'.format(operation_type)) @staticmethod def str(operation_type): @@ -27,8 +25,7 @@ class UnaryType: if operation_type == UnaryType.TILD: return '~' - logger.error('str: Unknown operation type {}'.format(operation_type)) - exit(-1) + raise SlithIRError('str: Unknown operation type {}'.format(operation_type)) class Unary(OperationWithLValue): diff --git a/slither/slithir/utils/ssa.py b/slither/slithir/utils/ssa.py index 74238589d..64acac84c 100644 --- a/slither/slithir/utils/ssa.py +++ b/slither/slithir/utils/ssa.py @@ -22,6 +22,7 @@ from slither.slithir.variables import (Constant, LocalIRVariable, ReferenceVariable, ReferenceVariableSSA, StateIRVariable, TemporaryVariable, TemporaryVariableSSA, TupleVariable, TupleVariableSSA) +from slither.slithir.exceptions import SlithIRError logger = logging.getLogger('SSA_Conversion') @@ -662,7 +663,6 @@ def copy_ir(ir, *instances): return Length(value, lvalue) - logger.error('Impossible ir copy on {} ({})'.format(ir, type(ir))) - exit(-1) + raise SlithIRError('Impossible ir copy on {} ({})'.format(ir, type(ir))) # endregion diff --git a/slither/solc_parsing/declarations/contract.py b/slither/solc_parsing/declarations/contract.py index 953210bf5..ff50df013 100644 --- a/slither/solc_parsing/declarations/contract.py +++ b/slither/solc_parsing/declarations/contract.py @@ -9,6 +9,7 @@ from slither.solc_parsing.declarations.modifier import ModifierSolc from slither.solc_parsing.declarations.structure import StructureSolc from slither.solc_parsing.solidity_types.type_parsing import parse_type from slither.solc_parsing.variables.state_variable import StateVariableSolc +from slither.solc_parsing.exceptions import ParsingError logger = logging.getLogger("ContractSolcParsing") @@ -186,8 +187,7 @@ class ContractSolc04(Contract): elif item[self.get_key()] == 'UsingForDirective': self._usingForNotParsed.append(item) else: - logger.error('Unknown contract item: '+item[self.get_key()]) - exit(-1) + raise ParsingError('Unknown contract item: '+item[self.get_key()]) return def _parse_struct(self, struct): diff --git a/slither/solc_parsing/declarations/function.py b/slither/solc_parsing/declarations/function.py index 6b95ca9f9..d862352ad 100644 --- a/slither/solc_parsing/declarations/function.py +++ b/slither/solc_parsing/declarations/function.py @@ -26,6 +26,7 @@ from slither.utils.expression_manipulations import SplitTernaryExpression from slither.utils.utils import unroll from slither.visitors.expression.export_values import ExportValues from slither.visitors.expression.has_conditional import HasConditional +from slither.solc_parsing.exceptions import ParsingError logger = logging.getLogger("FunctionSolc") @@ -725,8 +726,7 @@ class FunctionSolc(Function): link_nodes(node, new_node) node = new_node else: - logger.error('Statement not parsed %s'%name) - exit(-1) + raise ParsingError('Statement not parsed %s'%name) return node @@ -814,8 +814,7 @@ class FunctionSolc(Function): end_node = self._find_end_loop(node, [], 0) if not end_node: - logger.error('Break in no-loop context {}'.format(node)) - exit(-1) + raise ParsingError('Break in no-loop context {}'.format(node)) for son in node.sons: son.remove_father(node) @@ -826,8 +825,7 @@ class FunctionSolc(Function): start_node = self._find_start_loop(node, []) if not start_node: - logger.error('Continue in no-loop context {}'.format(node.nodeId())) - exit(-1) + raise ParsingError('Continue in no-loop context {}'.format(node.nodeId())) for son in node.sons: son.remove_father(node) diff --git a/slither/solc_parsing/exceptions.py b/slither/solc_parsing/exceptions.py new file mode 100644 index 000000000..f4f0e388f --- /dev/null +++ b/slither/solc_parsing/exceptions.py @@ -0,0 +1,7 @@ +from slither.exceptions import SlitherException + +class ParsingError(SlitherException): pass + +class ParsingNameReuse(SlitherException): pass + +class ParsingContractNotFound(SlitherException): pass \ No newline at end of file diff --git a/slither/solc_parsing/expressions/expression_parsing.py b/slither/solc_parsing/expressions/expression_parsing.py index 968ae446a..8a69a97f4 100644 --- a/slither/solc_parsing/expressions/expression_parsing.py +++ b/slither/solc_parsing/expressions/expression_parsing.py @@ -35,7 +35,7 @@ from slither.core.solidity_types import (ArrayType, ElementaryType, FunctionType, MappingType) from slither.solc_parsing.solidity_types.type_parsing import (UnknownType, parse_type) - +from slither.solc_parsing.exceptions import ParsingError logger = logging.getLogger("ExpressionParsing") @@ -78,8 +78,7 @@ def find_variable(var_name, caller_context, referenced_declaration=None): function = caller_context contract = function.contract else: - logger.error('Incorrect caller context') - exit(-1) + raise ParsingError('Incorrect caller context') if function: # We look for variable declared with the referencedDeclaration attr @@ -627,8 +626,7 @@ def parse_expression(expression, caller_context): elif type_name[caller_context.get_key()] == 'FunctionTypeName': array_type = parse_type(type_name, caller_context) else: - logger.error('Incorrect type array {}'.format(type_name)) - exit(-1) + raise ParsingError('Incorrect type array {}'.format(type_name)) array = NewArray(depth, array_type) return array @@ -664,5 +662,5 @@ def parse_expression(expression, caller_context): call = CallExpression(called, arguments, 'Modifier') return call - logger.error('Expression not parsed %s'%name) - exit(-1) + raise ParsingError('Expression not parsed %s'%name) + diff --git a/slither/solc_parsing/slitherSolc.py b/slither/solc_parsing/slitherSolc.py index 5f0e60b13..1b3651f6d 100644 --- a/slither/solc_parsing/slitherSolc.py +++ b/slither/solc_parsing/slitherSolc.py @@ -14,6 +14,7 @@ from slither.core.declarations.import_directive import Import from slither.analyses.data_dependency.data_dependency import compute_dependency from slither.utils.colors import red +from .exceptions import ParsingNameReuse, ParsingContractNotFound class SlitherSolc(Slither): @@ -182,8 +183,7 @@ class SlitherSolc(Slither): info += '\n{} is defined in:'.format(contract.name) info += '\n- {}\n- {}'.format(contract.source_mapping_str, self._contracts[contract.name].source_mapping_str) - logger.error(info) - exit(-1) + raise ParsingNameReuse(info) else: self._contracts_by_id[contract.id] = contract self._contracts[contract.name] = contract @@ -217,11 +217,11 @@ class SlitherSolc(Slither): father_constructors.append(self._contracts_by_id[i]) except KeyError: - logger.error(red('A contract was not found, it is likely that your codebase contains muliple contracts with the same name')) - logger.error(red('Truffle does not handle this case during compilation')) - logger.error(red('Please read https://github.com/trailofbits/slither/wiki#keyerror-or-nonetype-error')) - logger.error(red('And update your code to remove the duplicate')) - exit(-1) + txt = 'A contract was not found, it is likely that your codebase contains muliple contracts with the same name' + txt += 'Truffle does not handle this case during compilation' + txt += 'Please read https://github.com/trailofbits/slither/wiki#keyerror-or-nonetype-error' + txt += 'And update your code to remove the duplicate' + raise ParsingContractNotFound(txt) contract.setInheritance(ancestors, fathers, father_constructors) contracts_to_be_analyzed = self.contracts diff --git a/slither/solc_parsing/solidity_types/type_parsing.py b/slither/solc_parsing/solidity_types/type_parsing.py index 22e8954ab..af9a0bdb4 100644 --- a/slither/solc_parsing/solidity_types/type_parsing.py +++ b/slither/solc_parsing/solidity_types/type_parsing.py @@ -13,6 +13,7 @@ from slither.core.declarations.function import Function from slither.core.expressions.literal import Literal +from slither.solc_parsing.exceptions import ParsingError import re logger = logging.getLogger('TypeParsing') @@ -118,8 +119,7 @@ def _find_from_type_name(name, contract, contracts, structures, enums): return MappingType(from_type, to_type) if not var_type: - logger.error('Type not found '+str(name)) - exit(-1) + raise ParsingError('Type not found '+str(name)) return UserDefinedType(var_type) @@ -134,8 +134,7 @@ def parse_type(t, caller_context): elif isinstance(caller_context, Function): contract = caller_context.contract else: - logger.error('Incorrect caller context') - exit(-1) + raise ParsingError('Incorrect caller context') is_compact_ast = caller_context.is_compact_ast @@ -223,5 +222,4 @@ def parse_type(t, caller_context): return FunctionType(params_vars, return_values_vars) - logger.error('Type name not found '+str(t)) - exit(-1) + raise ParsingError('Type name not found '+str(t)) diff --git a/slither/solc_parsing/variables/variable_declaration.py b/slither/solc_parsing/variables/variable_declaration.py index 0f5f08ee3..72d8fc55e 100644 --- a/slither/solc_parsing/variables/variable_declaration.py +++ b/slither/solc_parsing/variables/variable_declaration.py @@ -6,7 +6,7 @@ from slither.core.variables.variable import Variable from slither.solc_parsing.solidity_types.type_parsing import parse_type, UnknownType from slither.core.solidity_types.elementary_type import ElementaryType, NonElementaryType - +from slither.solc_parsing.exceptions import ParsingError logger = logging.getLogger("VariableDeclarationSolcParsing") class MultipleVariablesDeclaration(Exception): @@ -51,8 +51,7 @@ class VariableDeclarationSolc(Variable): elif nodeType == 'VariableDeclaration': self._init_from_declaration(var, var['value']) else: - logger.error('Incorrect variable declaration type {}'.format(nodeType)) - exit(-1) + raise ParsingError('Incorrect variable declaration type {}'.format(nodeType)) else: nodeType = var['name'] @@ -65,15 +64,13 @@ class VariableDeclarationSolc(Variable): elif len(var['children']) > 2: raise MultipleVariablesDeclaration else: - logger.error('Variable declaration without children?'+var) - exit(-1) + raise ParsingError('Variable declaration without children?'+var) declaration = var['children'][0] self._init_from_declaration(declaration, init) elif nodeType == 'VariableDeclaration': self._init_from_declaration(var, None) else: - logger.error('Incorrect variable declaration type {}'.format(nodeType)) - exit(-1) + raise ParsingError('Incorrect variable declaration type {}'.format(nodeType)) @property def initialized(self): diff --git a/slither/visitors/expression/expression.py b/slither/visitors/expression/expression.py index fe8eeb821..6714508de 100644 --- a/slither/visitors/expression/expression.py +++ b/slither/visitors/expression/expression.py @@ -15,6 +15,7 @@ from slither.core.expressions.new_elementary_type import NewElementaryType from slither.core.expressions.tuple_expression import TupleExpression from slither.core.expressions.type_conversion import TypeConversion from slither.core.expressions.unary_operation import UnaryOperation +from slither.exceptions import SlitherError logger = logging.getLogger("ExpressionVisitor") @@ -86,8 +87,7 @@ class ExpressionVisitor: pass else: - logger.error('Expression not handled: {}'.format(expression)) - exit(-1) + raise SlitherError('Expression not handled: {}'.format(expression)) self._post_visit(expression) @@ -200,8 +200,7 @@ class ExpressionVisitor: pass else: - logger.error('Expression not handled: {}'.format(expression)) - exit(-1) + raise SlitherError('Expression not handled: {}'.format(expression)) # pre_expression_name @@ -302,8 +301,7 @@ class ExpressionVisitor: pass else: - logger.error('Expression not handled: {}'.format(expression)) - exit(-1) + raise SlitherError('Expression not handled: {}'.format(expression)) # post_expression_name diff --git a/slither/visitors/slithir/expression_to_slithir.py b/slither/visitors/slithir/expression_to_slithir.py index c5f275749..b1f5acbce 100644 --- a/slither/visitors/slithir/expression_to_slithir.py +++ b/slither/visitors/slithir/expression_to_slithir.py @@ -17,8 +17,7 @@ from slither.slithir.variables import (Constant, ReferenceVariable, TemporaryVariable, TupleVariable) from slither.visitors.expression.expression import ExpressionVisitor -#from slither.slithir.variables.state_variable import StateIRVariable -#from slither.slithir.variables.local_variable import LocalIRVariable +from slither.slithir.exceptions import SlithIRError logger = logging.getLogger("VISTIOR:ExpressionToSlithIR") @@ -57,8 +56,7 @@ def convert_assignment(left, right, t, return_type): elif t == AssignmentOperationType.ASSIGN_MODULO: return Binary(left, left, right, BinaryType.MODULO) - logger.error('Missing type during assignment conversion') - exit(-1) + raise SlithIRError('Missing type during assignment conversion') class ExpressionToSlithIR(ExpressionVisitor):