diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index 57f44bc56e..fab9562d20 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -92,3 +92,8 @@ from .operations.cache_array_length import CacheArrayLength from .statements.incorrect_using_for import IncorrectUsingFor from .operations.encode_packed import EncodePackedCollision +from .assembly.incorrect_return import IncorrectReturn +from .assembly.return_instead_of_leave import ReturnInsteadOfLeave +from .operations.incorrect_exp import IncorrectOperatorExponentiation +from .statements.tautological_compare import TautologicalCompare +from .statements.return_bomb import ReturnBomb diff --git a/slither/detectors/assembly/incorrect_return.py b/slither/detectors/assembly/incorrect_return.py new file mode 100644 index 0000000000..f5f0a98d9d --- /dev/null +++ b/slither/detectors/assembly/incorrect_return.py @@ -0,0 +1,91 @@ +from typing import List, Optional + +from slither.core.declarations import SolidityFunction, Function +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.slithir.operations import SolidityCall +from slither.utils.output import Output + + +def _assembly_node(function: Function) -> Optional[SolidityCall]: + """ + Check if there is a node that use return in assembly + + Args: + function: + + Returns: + + """ + + for ir in function.all_slithir_operations(): + if isinstance(ir, SolidityCall) and ir.function == SolidityFunction( + "return(uint256,uint256)" + ): + return ir + return None + + +class IncorrectReturn(AbstractDetector): + """ + Check for cases where a return(a,b) is used in an assembly function + """ + + ARGUMENT = "incorrect-return" + HELP = "If a `return` is incorrectly used in assembly mode." + IMPACT = DetectorClassification.HIGH + CONFIDENCE = DetectorClassification.MEDIUM + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-assembly-return" + + WIKI_TITLE = "Incorrect return in assembly" + WIKI_DESCRIPTION = "Detect if `return` in an assembly block halts unexpectedly the execution." + WIKI_EXPLOIT_SCENARIO = """ +```solidity +contract C { + function f() internal returns (uint a, uint b) { + assembly { + return (5, 6) + } + } + + function g() returns (bool){ + f(); + return true; + } +} +``` +The return statement in `f` will cause execution in `g` to halt. +The function will return 6 bytes starting from offset 5, instead of returning a boolean.""" + + WIKI_RECOMMENDATION = "Use the `leave` statement." + + # pylint: disable=too-many-nested-blocks + def _detect(self) -> List[Output]: + results: List[Output] = [] + for c in self.contracts: + for f in c.functions_and_modifiers_declared: + + for node in f.nodes: + if node.sons: + for function_called in node.internal_calls: + if isinstance(function_called, Function): + found = _assembly_node(function_called) + if found: + + info: DETECTOR_INFO = [ + f, + " calls ", + function_called, + " which halt the execution ", + found.node, + "\n", + ] + json = self.generate_result(info) + + results.append(json) + + return results diff --git a/slither/detectors/assembly/return_instead_of_leave.py b/slither/detectors/assembly/return_instead_of_leave.py new file mode 100644 index 0000000000..a1591d834b --- /dev/null +++ b/slither/detectors/assembly/return_instead_of_leave.py @@ -0,0 +1,68 @@ +from typing import List + +from slither.core.declarations import SolidityFunction, Function +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.slithir.operations import SolidityCall +from slither.utils.output import Output + + +class ReturnInsteadOfLeave(AbstractDetector): + """ + Check for cases where a return(a,b) is used in an assembly function that also returns two variables + """ + + ARGUMENT = "return-leave" + HELP = "If a `return` is used instead of a `leave`." + IMPACT = DetectorClassification.HIGH + CONFIDENCE = DetectorClassification.MEDIUM + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-assembly-return" + + WIKI_TITLE = "Return instead of leave in assembly" + WIKI_DESCRIPTION = "Detect if a `return` is used where a `leave` should be used." + WIKI_EXPLOIT_SCENARIO = """ +```solidity +contract C { + function f() internal returns (uint a, uint b) { + assembly { + return (5, 6) + } + } + +} +``` +The function will halt the execution, instead of returning a two uint.""" + + WIKI_RECOMMENDATION = "Use the `leave` statement." + + def _check_function(self, f: Function) -> List[Output]: + results: List[Output] = [] + + for node in f.nodes: + for ir in node.irs: + if isinstance(ir, SolidityCall) and ir.function == SolidityFunction( + "return(uint256,uint256)" + ): + info: DETECTOR_INFO = [f, " contains an incorrect call to return: ", node, "\n"] + json = self.generate_result(info) + + results.append(json) + return results + + def _detect(self) -> List[Output]: + results: List[Output] = [] + for c in self.contracts: + for f in c.functions_declared: + + if ( + len(f.returns) == 2 + and f.contains_assembly + and f.visibility not in ["public", "external"] + ): + results += self._check_function(f) + + return results diff --git a/slither/detectors/operations/incorrect_exp.py b/slither/detectors/operations/incorrect_exp.py new file mode 100644 index 0000000000..6188f5cb16 --- /dev/null +++ b/slither/detectors/operations/incorrect_exp.py @@ -0,0 +1,93 @@ +""" +Module detecting incorrect operator usage for exponentiation where bitwise xor '^' is used instead of '**' +""" +from typing import Tuple, List, Union + +from slither.core.cfg.node import Node +from slither.core.declarations import Contract, Function +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.slithir.operations import Binary, BinaryType, Operation +from slither.slithir.utils.utils import RVALUE +from slither.slithir.variables.constant import Constant +from slither.utils.output import Output + + +def _is_constant_candidate(var: Union[RVALUE, Function]) -> bool: + """ + Check if the variable is a constant. + Do not consider variable that are expressed with hexadecimal. + Something like 2^0xf is likely to be a correct bitwise operator + :param var: + :return: + """ + return isinstance(var, Constant) and not var.original_value.startswith("0x") + + +def _is_bitwise_xor_on_constant(ir: Operation) -> bool: + return ( + isinstance(ir, Binary) + and ir.type == BinaryType.CARET + and (_is_constant_candidate(ir.variable_left) or _is_constant_candidate(ir.variable_right)) + ) + + +def _detect_incorrect_operator(contract: Contract) -> List[Tuple[Function, Node]]: + ret: List[Tuple[Function, Node]] = [] + f: Function + for f in contract.functions + contract.modifiers: # type:ignore + # Heuristic: look for binary expressions with ^ operator where at least one of the operands is a constant, and + # the constant is not in hex, because hex typically is used with bitwise xor and not exponentiation + nodes = [node for node in f.nodes for ir in node.irs if _is_bitwise_xor_on_constant(ir)] + for node in nodes: + ret.append((f, node)) + return ret + + +# pylint: disable=too-few-public-methods +class IncorrectOperatorExponentiation(AbstractDetector): + """ + Incorrect operator usage of bitwise xor mistaking it for exponentiation + """ + + ARGUMENT = "incorrect-exp" + HELP = "Incorrect exponentiation" + IMPACT = DetectorClassification.HIGH + CONFIDENCE = DetectorClassification.MEDIUM + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#incorrect-exponentiation" + + WIKI_TITLE = "Incorrect exponentiation" + WIKI_DESCRIPTION = "Detect use of bitwise `xor ^` instead of exponential `**`" + WIKI_EXPLOIT_SCENARIO = """ +```solidity +contract Bug{ + uint UINT_MAX = 2^256 - 1; + ... +} +``` +Alice deploys a contract in which `UINT_MAX` incorrectly uses `^` operator instead of `**` for exponentiation""" + + WIKI_RECOMMENDATION = "Use the correct operator `**` for exponentiation." + + def _detect(self) -> List[Output]: + """Detect the incorrect operator usage for exponentiation where bitwise xor ^ is used instead of ** + + Returns: + list: (function, node) + """ + results: List[Output] = [] + for c in self.compilation_unit.contracts_derived: + res = _detect_incorrect_operator(c) + for (func, node) in res: + info: DETECTOR_INFO = [ + func, + " has bitwise-xor operator ^ instead of the exponentiation operator **: \n", + ] + info += ["\t - ", node, "\n"] + results.append(self.generate_result(info)) + + return results diff --git a/slither/detectors/statements/return_bomb.py b/slither/detectors/statements/return_bomb.py new file mode 100644 index 0000000000..8b6cd07a29 --- /dev/null +++ b/slither/detectors/statements/return_bomb.py @@ -0,0 +1,123 @@ +from typing import List + +from slither.core.cfg.node import Node +from slither.core.declarations import Contract +from slither.core.declarations.function import Function +from slither.core.solidity_types import Type +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.slithir.operations import LowLevelCall, HighLevelCall +from slither.analyses.data_dependency.data_dependency import is_tainted +from slither.utils.output import Output + + +class ReturnBomb(AbstractDetector): + + ARGUMENT = "return-bomb" + HELP = "A low level callee may consume all callers gas unexpectedly." + IMPACT = DetectorClassification.LOW + CONFIDENCE = DetectorClassification.MEDIUM + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#return-bomb" + + WIKI_TITLE = "Return Bomb" + WIKI_DESCRIPTION = "A low level callee may consume all callers gas unexpectedly." + WIKI_EXPLOIT_SCENARIO = """ +```solidity +//Modified from https://github.com/nomad-xyz/ExcessivelySafeCall +contract BadGuy { + function youveActivateMyTrapCard() external pure returns (bytes memory) { + assembly{ + revert(0, 1000000) + } + } +} + +contract Mark { + function oops(address badGuy) public{ + bool success; + bytes memory ret; + + // Mark pays a lot of gas for this copy + //(success, ret) = badGuy.call{gas:10000}( + (success, ret) = badGuy.call( + abi.encodeWithSelector( + BadGuy.youveActivateMyTrapCard.selector + ) + ); + + // Mark may OOG here, preventing local state changes + //importantCleanup(); + } +} + +``` +After Mark calls BadGuy bytes are copied from returndata to memory, the memory expansion cost is paid. This means that when using a standard solidity call, the callee can "returnbomb" the caller, imposing an arbitrary gas cost. +Callee unexpectedly makes the caller OOG. +""" + + WIKI_RECOMMENDATION = "Avoid unlimited implicit decoding of returndata." + + @staticmethod + def is_dynamic_type(ty: Type) -> bool: + # ty.is_dynamic ? + name = str(ty) + if "[]" in name or name in ("bytes", "string"): + return True + return False + + def get_nodes_for_function(self, function: Function, contract: Contract) -> List[Node]: + nodes = [] + for node in function.nodes: + for ir in node.irs: + if isinstance(ir, (HighLevelCall, LowLevelCall)): + if not is_tainted(ir.destination, contract): # type:ignore + # Only interested if the target address is controlled/tainted + continue + + if isinstance(ir, HighLevelCall) and isinstance(ir.function, Function): + # in normal highlevel calls return bombs are _possible_ + # if the return type is dynamic and the caller tries to copy and decode large data + has_dyn = False + if ir.function.return_type: + has_dyn = any( + self.is_dynamic_type(ty) for ty in ir.function.return_type + ) + + if not has_dyn: + continue + + # If a gas budget was specified then the + # user may not know about the return bomb + if ir.call_gas is None: + # if a gas budget was NOT specified then the caller + # may already suspect the call may spend all gas? + continue + + nodes.append(node) + # TODO: check that there is some state change after the call + + return nodes + + def _detect(self) -> List[Output]: + results = [] + + for contract in self.compilation_unit.contracts: + for function in contract.functions_declared: + nodes = self.get_nodes_for_function(function, contract) + if nodes: + info: DETECTOR_INFO = [ + function, + " tries to limit the gas of an external call that controls implicit decoding\n", + ] + + for node in sorted(nodes, key=lambda x: x.node_id): + info += ["\t", node, "\n"] + + res = self.generate_result(info) + results.append(res) + + return results diff --git a/slither/detectors/statements/tautological_compare.py b/slither/detectors/statements/tautological_compare.py new file mode 100644 index 0000000000..0dc34b582f --- /dev/null +++ b/slither/detectors/statements/tautological_compare.py @@ -0,0 +1,69 @@ +from typing import List +from slither.detectors.abstract_detector import ( + AbstractDetector, + DetectorClassification, + DETECTOR_INFO, +) +from slither.slithir.operations import ( + Binary, + BinaryType, +) + +from slither.core.declarations import Function +from slither.utils.output import Output + + +class TautologicalCompare(AbstractDetector): + """ + Same variable comparison detector + """ + + ARGUMENT = "tautological-compare" + HELP = "Comparing a variable to itself always returns true or false, depending on comparison" + IMPACT = DetectorClassification.MEDIUM + CONFIDENCE = DetectorClassification.HIGH + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#tautological-compare" + + WIKI_TITLE = "Tautological compare" + WIKI_DESCRIPTION = "A variable compared to itself is probably an error as it will always return `true` for `==`, `>=`, `<=` and always `false` for `<`, `>` and `!=`." + WIKI_EXPLOIT_SCENARIO = """ +```solidity + function check(uint a) external returns(bool){ + return (a >= a); + } +``` +`check` always return true.""" + + WIKI_RECOMMENDATION = "Remove comparison or compare to different value." + + def _check_function(self, f: Function) -> List[Output]: + affected_nodes = set() + for node in f.nodes: + for ir in node.irs: + if isinstance(ir, Binary): + if ir.type in [ + BinaryType.GREATER, + BinaryType.GREATER_EQUAL, + BinaryType.LESS, + BinaryType.LESS_EQUAL, + BinaryType.EQUAL, + BinaryType.NOT_EQUAL, + ]: + if ir.variable_left == ir.variable_right: + affected_nodes.add(node) + + results = [] + for n in affected_nodes: + info: DETECTOR_INFO = [f, " compares a variable to itself:\n\t", n, "\n"] + res = self.generate_result(info) + results.append(res) + return results + + def _detect(self): + results = [] + + for f in self.compilation_unit.functions_and_modifiers: + results.extend(self._check_function(f)) + + return results diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectOperatorExponentiation_0_7_6_incorrect_exp_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectOperatorExponentiation_0_7_6_incorrect_exp_sol__0.txt new file mode 100644 index 0000000000..458dd3bdcd --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectOperatorExponentiation_0_7_6_incorrect_exp_sol__0.txt @@ -0,0 +1,9 @@ +Test.bad1() (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#9-12) has bitwise-xor operator ^ instead of the exponentiation operator **: + - UINT_MAX = 2 ^ 256 - 1 (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#10) + +Test.bad0(uint256) (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#5-7) has bitwise-xor operator ^ instead of the exponentiation operator **: + - a ^ 2 (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#6) + +Derived.slitherConstructorVariables() (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#30) has bitwise-xor operator ^ instead of the exponentiation operator **: + - my_var = 2 ^ 256 - 1 (tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol#3) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_IncorrectReturn_0_8_10_incorrect_return_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectReturn_0_8_10_incorrect_return_sol__0.txt new file mode 100644 index 0000000000..5d87cd932d --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_IncorrectReturn_0_8_10_incorrect_return_sol__0.txt @@ -0,0 +1,4 @@ +C.bad1() (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#21-24) calls C.indirect() (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#17-19) which halt the execution return(uint256,uint256)(5,6) (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#4) + +C.bad0() (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#8-11) calls C.internal_return() (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#2-6) which halt the execution return(uint256,uint256)(5,6) (tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol#4) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ReturnBomb_0_8_20_return_bomb_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ReturnBomb_0_8_20_return_bomb_sol__0.txt new file mode 100644 index 0000000000..f53b5233fd --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_ReturnBomb_0_8_20_return_bomb_sol__0.txt @@ -0,0 +1,5 @@ +Mark.oops(address) (tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol#31-55) tries to limit the gas of an external call that controls implicit decoding + ret1 = BadGuy(badGuy).fbad{gas: 2000}() (tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol#42) + (x,str) = BadGuy(badGuy).fbad1{gas: 2000}() (tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol#44) + (success,ret) = badGuy.call{gas: 10000}(abi.encodeWithSelector(BadGuy.llbad.selector)) (tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol#47-51) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_ReturnInsteadOfLeave_0_8_10_incorrect_return_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_ReturnInsteadOfLeave_0_8_10_incorrect_return_sol__0.txt new file mode 100644 index 0000000000..28f579f818 --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_ReturnInsteadOfLeave_0_8_10_incorrect_return_sol__0.txt @@ -0,0 +1,2 @@ +C.f() (tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol#3-7) contains an incorrect call to return: return(uint256,uint256)(5,6) (tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol#5) + diff --git a/tests/e2e/detectors/snapshots/detectors__detector_TautologicalCompare_0_8_20_compare_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_TautologicalCompare_0_8_20_compare_sol__0.txt new file mode 100644 index 0000000000..76685043ce --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_TautologicalCompare_0_8_20_compare_sol__0.txt @@ -0,0 +1,3 @@ +A.check(uint256) (tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol#3-5) compares a variable to itself: + (a >= a) (tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol#4) + diff --git a/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol b/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol new file mode 100644 index 0000000000..b930ee5c1f --- /dev/null +++ b/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol @@ -0,0 +1,30 @@ +contract Test { + + uint my_var = 2 ^ 256-1; + + function bad0(uint a) internal returns (uint) { + return a^2; + } + + function bad1() internal returns (uint) { + uint UINT_MAX = 2^256-1; + return UINT_MAX; + } + + /* Correct exponentiation operator */ + function good0(uint a) internal returns (uint) { + return a**2; + } + + /* Neither operand is a constant */ + function good1(uint a) internal returns (uint) { + return a^a; + } + + /* The constant operand 0xff in hex typically means bitwise xor */ + function good2(uint a) internal returns (uint) { + return a^0xff; + } +} + +contract Derived is Test {} diff --git a/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol-0.7.6.zip b/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol-0.7.6.zip new file mode 100644 index 0000000000..c6fea01511 Binary files /dev/null and b/tests/e2e/detectors/test_data/incorrect-exp/0.7.6/incorrect_exp.sol-0.7.6.zip differ diff --git a/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol b/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol new file mode 100644 index 0000000000..e81a747ba1 --- /dev/null +++ b/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol @@ -0,0 +1,36 @@ +contract C { + function internal_return() internal{ + assembly { + return (5, 6) + } + } + + function bad0() public returns (bool){ + internal_return(); + return true; + } + + function indirect2() internal { + internal_return(); + } + + function indirect() internal { + indirect2(); + } + + function bad1() public returns (bool){ + indirect(); + return true; + } + + function good0() public{ + // Dont report if there is no following operation + internal_return(); + } + + function good1() public returns (uint a, uint b){ + assembly { + return (5, 6) + } + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol-0.8.10.zip b/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol-0.8.10.zip new file mode 100644 index 0000000000..2491a10a65 Binary files /dev/null and b/tests/e2e/detectors/test_data/incorrect-return/0.8.10/incorrect_return.sol-0.8.10.zip differ diff --git a/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol b/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol new file mode 100644 index 0000000000..76413fdcaa --- /dev/null +++ b/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol @@ -0,0 +1,57 @@ +contract BadGuy { + function llbad() external pure returns (bytes memory) { + assembly{ + revert(0, 1000000) + } + } + + function fgood() external payable returns (uint){ + assembly{ + return(0, 1000000) + } + } + + function fbad() external payable returns (uint[] memory){ + assembly{ + return(0, 1000000) + } + } + + function fbad1() external payable returns (uint, string memory){ + assembly{ + return(0, 1000000) + } + } + + +} + +contract Mark { + + function oops(address badGuy) public{ + bool success; + string memory str; + bytes memory ret; + uint x; + uint[] memory ret1; + + x = BadGuy(badGuy).fgood{gas:2000}(); + + ret1 = BadGuy(badGuy).fbad(); //good (no gas specified) + + ret1 = BadGuy(badGuy).fbad{gas:2000}(); + + (x, str) = BadGuy(badGuy).fbad1{gas:2000}(); + + // Mark pays a lot of gas for this copy 😬😬😬 + (success, ret) = badGuy.call{gas:10000}( + abi.encodeWithSelector( + BadGuy.llbad.selector + ) + ); + + // Mark may OOG here, preventing local state changes + //importantCleanup(); + } +} + diff --git a/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol-0.8.20.zip b/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol-0.8.20.zip new file mode 100644 index 0000000000..4c10ea6fea Binary files /dev/null and b/tests/e2e/detectors/test_data/return-bomb/0.8.20/return_bomb.sol-0.8.20.zip differ diff --git a/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol b/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol new file mode 100644 index 0000000000..f02d7f3791 --- /dev/null +++ b/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol @@ -0,0 +1,8 @@ +contract C { + + function f() internal returns (uint a, uint b){ + assembly { + return (5, 6) + } + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol-0.8.10.zip b/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol-0.8.10.zip new file mode 100644 index 0000000000..f4c389fcfb Binary files /dev/null and b/tests/e2e/detectors/test_data/return-leave/0.8.10/incorrect_return.sol-0.8.10.zip differ diff --git a/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol b/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol new file mode 100644 index 0000000000..ede2f0fc18 --- /dev/null +++ b/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol @@ -0,0 +1,6 @@ + +contract A{ + function check(uint a) external returns(bool){ + return (a >= a); + } +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol-0.8.20.zip b/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol-0.8.20.zip new file mode 100644 index 0000000000..05b78fad08 Binary files /dev/null and b/tests/e2e/detectors/test_data/tautological-compare/0.8.20/compare.sol-0.8.20.zip differ diff --git a/tests/e2e/detectors/test_detectors.py b/tests/e2e/detectors/test_detectors.py index 6fc04e4e1f..28dcc5e755 100644 --- a/tests/e2e/detectors/test_detectors.py +++ b/tests/e2e/detectors/test_detectors.py @@ -1654,6 +1654,31 @@ def id_test(test_item: Test): "encode_packed_collision.sol", "0.7.6", ), + Test( + all_detectors.IncorrectReturn, + "incorrect_return.sol", + "0.8.10", + ), + Test( + all_detectors.ReturnInsteadOfLeave, + "incorrect_return.sol", + "0.8.10", + ), + Test( + all_detectors.IncorrectOperatorExponentiation, + "incorrect_exp.sol", + "0.7.6", + ), + Test( + all_detectors.TautologicalCompare, + "compare.sol", + "0.8.20", + ), + Test( + all_detectors.ReturnBomb, + "return_bomb.sol", + "0.8.20", + ), ] GENERIC_PATH = "/GENERIC_PATH" diff --git a/tests/e2e/solc_parsing/test_ast_parsing.py b/tests/e2e/solc_parsing/test_ast_parsing.py index 2da4cf1e26..37f6d7274b 100644 --- a/tests/e2e/solc_parsing/test_ast_parsing.py +++ b/tests/e2e/solc_parsing/test_ast_parsing.py @@ -4,13 +4,13 @@ import sys from pathlib import Path from typing import List, Dict, Tuple -from packaging.version import parse as parse_version -import pytest + +from crytic_compile import CryticCompile, save_to_zip +from crytic_compile.utils.zip import load_from_zip from deepdiff import DeepDiff +from packaging.version import parse as parse_version from solc_select.solc_select import install_artifacts as install_solc_versions from solc_select.solc_select import installed_versions as get_installed_solc_versions -from crytic_compile import CryticCompile, save_to_zip -from crytic_compile.utils.zip import load_from_zip from slither import Slither from slither.printers.guidance.echidna import Echidna