Merge branch 'dev' into detector_for_invalid_using_for

pull/1653/head
Feist Josselin 2 years ago
commit 560319ab04
  1. 1
      .github/workflows/ci.yml
  2. 2
      .github/workflows/pip-audit.yml
  3. 10
      examples/flat/a.sol
  4. 13
      examples/flat/b.sol
  5. 10
      scripts/ci_test_flat.sh
  6. 95
      scripts/ci_test_interface.sh
  7. 8
      setup.py
  8. 1
      slither/analyses/data_dependency/data_dependency.py
  9. 6
      slither/core/compilation_unit.py
  10. 7
      slither/core/declarations/custom_error.py
  11. 4
      slither/core/declarations/solidity_variables.py
  12. 1
      slither/detectors/all_detectors.py
  13. 4
      slither/detectors/assembly/shift_parameter_mixup.py
  14. 104
      slither/detectors/operations/encode_packed.py
  15. 42
      slither/detectors/operations/unused_return_values.py
  16. 31
      slither/detectors/reentrancy/reentrancy_events.py
  17. 23
      slither/detectors/statements/incorrect_strict_equality.py
  18. 11
      slither/detectors/variables/uninitialized_local_variables.py
  19. 9
      slither/slithir/convert.py
  20. 2
      slither/slithir/operations/assignment.py
  21. 1
      slither/slithir/operations/binary.py
  22. 5
      slither/slithir/operations/new_array.py
  23. 3
      slither/slithir/operations/new_contract.py
  24. 3
      slither/slithir/operations/new_structure.py
  25. 2
      slither/slithir/operations/unary.py
  26. 1
      slither/solc_parsing/declarations/function.py
  27. 3
      slither/solc_parsing/declarations/modifier.py
  28. 2
      slither/tools/flattening/export/export.py
  29. 45
      slither/tools/flattening/flattening.py
  30. 0
      slither/tools/interface/__init__.py
  31. 105
      slither/tools/interface/__main__.py
  32. 2
      slither/tools/read_storage/read_storage.py
  33. 181
      slither/utils/code_generation.py
  34. 6
      slither/utils/myprettytable.py
  35. 15
      slither/utils/type.py
  36. 1
      slither/visitors/slithir/expression_to_slithir.py
  37. 15
      tests/e2e/detectors/snapshots/detectors__detector_EncodePackedCollision_0_7_6_encode_packed_collision_sol__0.txt
  38. 2
      tests/e2e/detectors/snapshots/detectors__detector_ShiftParameterMixup_0_7_6_shift_parameter_mixup_sol__0.txt
  39. 8
      tests/e2e/detectors/snapshots/detectors__detector_UnusedReturnValues_0_4_25_unused_return_sol__0.txt
  40. 8
      tests/e2e/detectors/snapshots/detectors__detector_UnusedReturnValues_0_5_16_unused_return_sol__0.txt
  41. 8
      tests/e2e/detectors/snapshots/detectors__detector_UnusedReturnValues_0_6_11_unused_return_sol__0.txt
  42. 8
      tests/e2e/detectors/snapshots/detectors__detector_UnusedReturnValues_0_7_6_unused_return_sol__0.txt
  43. 78
      tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol
  44. BIN
      tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol-0.7.6.zip
  45. 24
      tests/e2e/detectors/test_data/incorrect-equality/0.7.6/incorrect_equality.sol
  46. BIN
      tests/e2e/detectors/test_data/incorrect-equality/0.7.6/incorrect_equality.sol-0.7.6.zip
  47. 3
      tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol
  48. BIN
      tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol-0.7.6.zip
  49. 11
      tests/e2e/detectors/test_data/uninitialized-local/0.4.25/uninitialized_local_variable.sol
  50. BIN
      tests/e2e/detectors/test_data/uninitialized-local/0.4.25/uninitialized_local_variable.sol-0.4.25.zip
  51. 11
      tests/e2e/detectors/test_data/uninitialized-local/0.5.16/uninitialized_local_variable.sol
  52. BIN
      tests/e2e/detectors/test_data/uninitialized-local/0.5.16/uninitialized_local_variable.sol-0.5.16.zip
  53. 11
      tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol
  54. BIN
      tests/e2e/detectors/test_data/uninitialized-local/0.6.11/uninitialized_local_variable.sol-0.6.11.zip
  55. 11
      tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol
  56. BIN
      tests/e2e/detectors/test_data/uninitialized-local/0.7.6/uninitialized_local_variable.sol-0.7.6.zip
  57. 8
      tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol
  58. BIN
      tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol-0.4.25.zip
  59. 8
      tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol
  60. BIN
      tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol-0.5.16.zip
  61. 8
      tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol
  62. BIN
      tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol-0.6.11.zip
  63. 8
      tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol
  64. BIN
      tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol-0.7.6.zip
  65. 5
      tests/e2e/detectors/test_detectors.py
  66. 1
      tests/e2e/solc_parsing/test_ast_parsing.py
  67. 9
      tests/e2e/solc_parsing/test_data/assembly-all.sol
  68. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.0-legacy.zip
  69. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.1-legacy.zip
  70. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.10-legacy.zip
  71. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.11-legacy.zip
  72. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.12-compact.zip
  73. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.12-legacy.zip
  74. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.13-compact.zip
  75. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.13-legacy.zip
  76. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.14-compact.zip
  77. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.14-legacy.zip
  78. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.15-compact.zip
  79. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.15-legacy.zip
  80. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.16-compact.zip
  81. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.16-legacy.zip
  82. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.17-compact.zip
  83. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.17-legacy.zip
  84. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.18-compact.zip
  85. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.18-legacy.zip
  86. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.19-compact.zip
  87. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.19-legacy.zip
  88. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.2-legacy.zip
  89. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.20-compact.zip
  90. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.20-legacy.zip
  91. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.21-compact.zip
  92. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.21-legacy.zip
  93. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.22-compact.zip
  94. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.22-legacy.zip
  95. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.23-compact.zip
  96. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.23-legacy.zip
  97. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.24-compact.zip
  98. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.24-legacy.zip
  99. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.25-compact.zip
  100. BIN
      tests/e2e/solc_parsing/test_data/compile/assembly-all.sol-0.4.25-legacy.zip
  101. Some files were not shown because too many files have changed in this diff Show More

@ -36,6 +36,7 @@ jobs:
"etherscan",
"find_paths",
"flat",
"interface",
"kspec",
"printers",
# "prop"

@ -34,6 +34,6 @@ jobs:
python -m pip install .
- name: Run pip-audit
uses: trailofbits/gh-action-pip-audit@v0.0.4
uses: pypa/gh-action-pip-audit@v1.0.7
with:
virtual-environment: /tmp/pip-audit-env

@ -1,3 +1,9 @@
contract A{
pragma solidity 0.8.19;
}
error RevertIt();
contract Example {
function reverts() external pure {
revert RevertIt();
}
}

@ -1,5 +1,16 @@
import "./a.sol";
contract B is A{
pragma solidity 0.8.19;
enum B {
a,
b
}
contract T {
Example e = new Example();
function b() public returns(uint) {
B b = B.a;
return 4;
}
}

@ -1,6 +1,8 @@
#!/usr/bin/env bash
shopt -s extglob
### Test slither-prop
### Test slither-flat
solc-select use 0.8.19 --always-install
cd examples/flat || exit 1
@ -8,5 +10,11 @@ if ! slither-flat b.sol; then
echo "slither-flat failed"
exit 1
fi
SUFFIX="@(sol)"
if ! solc "crytic-export/flattening/"*$SUFFIX; then
echo "solc failed on flattened files"
exit 1
fi
exit 0

@ -0,0 +1,95 @@
#!/usr/bin/env bash
### Test slither-interface
DIR_TESTS="tests/tools/interface"
solc-select use 0.8.19 --always-install
#Test 1 - Etherscan target
slither-interface WETH9 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
DIFF=$(diff crytic-export/interfaces/IWETH9.sol "$DIR_TESTS/test_1.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 1 failed"
cat "crytic-export/interfaces/IWETH9.sol"
echo ""
cat "$DIR_TESTS/test_1.sol"
exit 255
fi
#Test 2 - Local file target
slither-interface Mock tests/tools/interface/ContractMock.sol
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_2.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 2 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_2.sol"
exit 255
fi
#Test 3 - unroll structs
slither-interface Mock tests/tools/interface/ContractMock.sol --unroll-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_3.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 3 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_3.sol"
exit 255
fi
#Test 4 - exclude structs
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-structs
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_4.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 4 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_4.sol"
exit 255
fi
#Test 5 - exclude errors
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-errors
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_5.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 5 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_5.sol"
exit 255
fi
#Test 6 - exclude enums
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-enums
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_6.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 6 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_6.sol"
exit 255
fi
#Test 7 - exclude events
slither-interface Mock tests/tools/interface/ContractMock.sol --exclude-events
DIFF=$(diff crytic-export/interfaces/IMock.sol "$DIR_TESTS/test_7.sol" --strip-trailing-cr)
if [ "$DIFF" != "" ]
then
echo "slither-interface test 7 failed"
cat "crytic-export/interfaces/IMock.sol"
echo ""
cat "$DIR_TESTS/test_7.sol"
exit 255
fi
rm -r crytic-export

@ -13,11 +13,14 @@ setup(
python_requires=">=3.8",
install_requires=[
"packaging",
"prettytable>=0.7.2",
"prettytable>=3.3.0",
"pycryptodome>=3.4.6",
# "crytic-compile>=0.3.1,<0.4.0",
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@windows-rel-path#egg=crytic-compile",
"crytic-compile@git+https://github.com/crytic/crytic-compile.git@dev#egg=crytic-compile",
"web3>=6.0.0",
"eth-abi>=4.0.0",
"eth-typing>=3.0.0",
"eth-utils>=2.1.0",
],
extras_require={
"lint": [
@ -61,6 +64,7 @@ setup(
"slither-read-storage = slither.tools.read_storage.__main__:main",
"slither-doctor = slither.tools.doctor.__main__:main",
"slither-documentation = slither.tools.documentation.__main__:main",
"slither-interface = slither.tools.interface.__main__:main",
]
},
)

@ -129,6 +129,7 @@ GENERIC_TAINT = {
SolidityVariableComposed("msg.value"),
SolidityVariableComposed("msg.data"),
SolidityVariableComposed("tx.origin"),
SolidityVariableComposed("tx.gasprice"),
}

@ -13,7 +13,7 @@ from slither.core.declarations import (
Function,
Modifier,
)
from slither.core.declarations.custom_error import CustomError
from slither.core.declarations.custom_error_top_level import CustomErrorTopLevel
from slither.core.declarations.enum_top_level import EnumTopLevel
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.structure_top_level import StructureTopLevel
@ -46,7 +46,7 @@ class SlitherCompilationUnit(Context):
self._using_for_top_level: List[UsingForTopLevel] = []
self._pragma_directives: List[Pragma] = []
self._import_directives: List[Import] = []
self._custom_errors: List[CustomError] = []
self._custom_errors: List[CustomErrorTopLevel] = []
self._user_defined_value_types: Dict[str, TypeAliasTopLevel] = {}
self._all_functions: Set[Function] = set()
@ -216,7 +216,7 @@ class SlitherCompilationUnit(Context):
return self._using_for_top_level
@property
def custom_errors(self) -> List[CustomError]:
def custom_errors(self) -> List[CustomErrorTopLevel]:
return self._custom_errors
@property

@ -1,8 +1,8 @@
from typing import List, TYPE_CHECKING, Optional, Type
from slither.core.solidity_types import UserDefinedType
from slither.core.source_mapping.source_mapping import SourceMapping
from slither.core.variables.local_variable import LocalVariable
from slither.utils.type import is_underlying_type_address
if TYPE_CHECKING:
from slither.core.compilation_unit import SlitherCompilationUnit
@ -43,10 +43,7 @@ class CustomError(SourceMapping):
@staticmethod
def _convert_type_for_solidity_signature(t: Optional[Type]) -> str:
# pylint: disable=import-outside-toplevel
from slither.core.declarations import Contract
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
if is_underlying_type_address(t):
return "address"
return str(t)

@ -201,6 +201,10 @@ class SolidityCustomRevert(SolidityFunction):
self._custom_error = custom_error
self._return_type: List[Union[TypeInformation, ElementaryType]] = []
@property
def custom_error(self) -> CustomError:
return self._custom_error
def __eq__(self, other: Any) -> bool:
return (
self.__class__ == other.__class__

@ -90,3 +90,4 @@ from .functions.permit_domain_signature_collision import DomainSeparatorCollisio
from .functions.codex import Codex
from .functions.cyclomatic_complexity import CyclomaticComplexity
from .statements.incorrect_using_for import IncorrectUsingFor
from .operations.encode_packed import EncodePackedCollision

@ -52,7 +52,9 @@ The shift statement will right-shift the constant 8 by `a` bits"""
BinaryType.LEFT_SHIFT,
BinaryType.RIGHT_SHIFT,
]:
if isinstance(ir.variable_left, Constant):
if isinstance(ir.variable_left, Constant) and not isinstance(
ir.variable_right, Constant
):
info: DETECTOR_INFO = [
f,
" contains an incorrect shift operation: ",

@ -0,0 +1,104 @@
"""
Module detecting usage of more than one dynamic type in abi.encodePacked() arguments which could lead to collision
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.core.declarations.solidity_variables import SolidityFunction
from slither.slithir.operations import SolidityCall
from slither.analyses.data_dependency.data_dependency import is_tainted
from slither.core.solidity_types import ElementaryType
from slither.core.solidity_types import ArrayType
def _is_dynamic_type(arg):
"""
Args:
arg (function argument)
Returns:
Bool
"""
if isinstance(arg.type, ElementaryType) and (arg.type.name in ["string", "bytes"]):
return True
if isinstance(arg.type, ArrayType) and arg.type.length is None:
return True
return False
def _detect_abi_encodePacked_collision(contract):
"""
Args:
contract (Contract)
Returns:
list((Function), (list (Node)))
"""
ret = []
# pylint: disable=too-many-nested-blocks
for f in contract.functions_and_modifiers_declared:
for n in f.nodes:
for ir in n.irs:
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction(
"abi.encodePacked()"
):
dynamic_type_count = 0
for arg in ir.arguments:
if is_tainted(arg, contract) and _is_dynamic_type(arg):
dynamic_type_count += 1
elif dynamic_type_count > 1:
ret.append((f, n))
dynamic_type_count = 0
else:
dynamic_type_count = 0
if dynamic_type_count > 1:
ret.append((f, n))
return ret
class EncodePackedCollision(AbstractDetector):
"""
Detect usage of more than one dynamic type in abi.encodePacked() arguments which could to collision
"""
ARGUMENT = "encode-packed-collision"
HELP = "ABI encodePacked Collision"
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
WIKI = (
"https://github.com/crytic/slither/wiki/Detector-Documentation#abi-encodePacked-collision"
)
WIKI_TITLE = "ABI encodePacked Collision"
WIKI_DESCRIPTION = """Detect collision due to dynamic type usages in `abi.encodePacked`"""
WIKI_EXPLOIT_SCENARIO = """
```solidity
contract Sign {
function get_hash_for_signature(string name, string doc) external returns(bytes32) {
return keccak256(abi.encodePacked(name, doc));
}
}
```
Bob calls `get_hash_for_signature` with (`bob`, `This is the content`). The hash returned is used as an ID.
Eve creates a collision with the ID using (`bo`, `bThis is the content`) and compromises the system.
"""
WIKI_RECOMMENDATION = """Do not use more than one dynamic type in `abi.encodePacked()`
(see the [Solidity documentation](https://solidity.readthedocs.io/en/v0.5.10/abi-spec.html?highlight=abi.encodePacked#non-standard-packed-modeDynamic)).
Use `abi.encode()`, preferably."""
def _detect(self):
"""Detect usage of more than one dynamic type in abi.encodePacked(..) arguments which could lead to collision"""
results = []
for c in self.compilation_unit.contracts:
values = _detect_abi_encodePacked_collision(c)
for func, node in values:
info = [
func,
" calls abi.encodePacked() with multiple dynamic arguments:\n\t- ",
node,
"\n",
]
json = self.generate_result(info)
results.append(json)
return results

@ -3,7 +3,7 @@ Module detecting unused return values from external calls
"""
from typing import List
from slither.core.cfg.node import Node
from slither.core.cfg.node import Node, NodeType
from slither.core.declarations import Function
from slither.core.declarations.function_contract import FunctionContract
from slither.core.variables.state_variable import StateVariable
@ -12,8 +12,8 @@ from slither.detectors.abstract_detector import (
DetectorClassification,
DETECTOR_INFO,
)
from slither.slithir.operations import HighLevelCall
from slither.slithir.operations.operation import Operation
from slither.slithir.operations import HighLevelCall, Assignment, Unpack, Operation
from slither.slithir.variables import TupleVariable
from slither.utils.output import Output
@ -50,13 +50,18 @@ contract MyConc{
WIKI_RECOMMENDATION = "Ensure that all the return values of the function calls are used."
def _is_instance(self, ir: Operation) -> bool: # pylint: disable=no-self-use
return isinstance(ir, HighLevelCall) and (
(
isinstance(ir.function, Function)
and ir.function.solidity_signature
not in ["transfer(address,uint256)", "transferFrom(address,address,uint256)"]
return (
isinstance(ir, HighLevelCall)
and (
(
isinstance(ir.function, Function)
and ir.function.solidity_signature
not in ["transfer(address,uint256)", "transferFrom(address,address,uint256)"]
)
or not isinstance(ir.function, Function)
)
or not isinstance(ir.function, Function)
or ir.node.type == NodeType.TRY
and isinstance(ir, (Assignment, Unpack))
)
def detect_unused_return_values(
@ -71,18 +76,27 @@ contract MyConc{
"""
values_returned = []
nodes_origin = {}
# pylint: disable=too-many-nested-blocks
for n in f.nodes:
for ir in n.irs:
if self._is_instance(ir):
# if a return value is stored in a state variable, it's ok
if ir.lvalue and not isinstance(ir.lvalue, StateVariable):
values_returned.append(ir.lvalue)
values_returned.append((ir.lvalue, None))
nodes_origin[ir.lvalue] = ir
if isinstance(ir.lvalue, TupleVariable):
# we iterate the number of elements the tuple has
# and add a (variable, index) in values_returned for each of them
for index in range(len(ir.lvalue.type)):
values_returned.append((ir.lvalue, index))
for read in ir.read:
if read in values_returned:
values_returned.remove(read)
return [nodes_origin[value].node for value in values_returned]
remove = (read, ir.index) if isinstance(ir, Unpack) else (read, None)
if remove in values_returned:
# this is needed to remove the tuple variable when the first time one of its element is used
if remove[1] is not None and (remove[0], None) in values_returned:
values_returned.remove((remove[0], None))
values_returned.remove(remove)
return [nodes_origin[value].node for (value, _) in values_returned]
def _detect(self) -> List[Output]:
"""Detect high level calls which return a value that are never used"""

@ -29,24 +29,45 @@ class ReentrancyEvent(Reentrancy):
# region wiki_description
WIKI_DESCRIPTION = """
Detection of the [reentrancy bug](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy).
Only report reentrancies leading to out-of-order events."""
Detects [reentrancies](https://github.com/trailofbits/not-so-smart-contracts/tree/master/reentrancy) that allow manipulation of the order or value of events."""
# endregion wiki_description
# region wiki_exploit_scenario
WIKI_EXPLOIT_SCENARIO = """
```solidity
function bug(Called d){
contract ReentrantContract {
function f() external {
if (BugReentrancyEvents(msg.sender).counter() == 1) {
BugReentrancyEvents(msg.sender).count(this);
}
}
}
contract Counter {
uint public counter;
event Counter(uint);
}
contract BugReentrancyEvents is Counter {
function count(ReentrantContract d) external {
counter += 1;
d.f();
emit Counter(counter);
}
}
contract NoReentrancyEvents is Counter {
function count(ReentrantContract d) external {
counter += 1;
emit Counter(counter);
d.f();
}
}
```
If `d.()` re-enters, the `Counter` events will be shown in an incorrect order, which might lead to issues for third parties."""
If the external call `d.f()` re-enters `BugReentrancyEvents`, the `Counter` events will be incorrect (`Counter(2)`, `Counter(2)`) whereas `NoReentrancyEvents` will correctly emit
(`Counter(1)`, `Counter(2)`). This may cause issues for offchain components that rely on the values of events e.g. checking for the amount deposited to a bridge."""
# endregion wiki_exploit_scenario
WIKI_RECOMMENDATION = "Apply the [`check-effects-interactions` pattern](http://solidity.readthedocs.io/en/v0.4.21/security-considerations.html#re-entrancy)."
WIKI_RECOMMENDATION = "Apply the [`check-effects-interactions` pattern](https://docs.soliditylang.org/en/latest/security-considerations.html#re-entrancy)."
STANDARD_JSON = False

@ -31,6 +31,7 @@ from slither.slithir.variables.constant import Constant
from slither.slithir.variables.local_variable import LocalIRVariable
from slither.slithir.variables.temporary_ssa import TemporaryVariableSSA
from slither.utils.output import Output
from slither.utils.type import is_underlying_type_address
class IncorrectStrictEquality(AbstractDetector):
@ -72,6 +73,19 @@ contract Crowdsale{
def is_direct_comparison(ir: Operation) -> bool:
return isinstance(ir, Binary) and ir.type == BinaryType.EQUAL
@staticmethod
def is_not_comparing_addresses(ir: Binary) -> bool:
"""
Comparing addresses strictly should not be flagged.
"""
if is_underlying_type_address(ir.variable_left.type) and is_underlying_type_address(
ir.variable_right.type
):
return False
return True
@staticmethod
def is_any_tainted(
variables: List[
@ -108,7 +122,6 @@ contract Crowdsale{
):
taints.append(ir.lvalue)
if isinstance(ir, HighLevelCall):
# print(ir.function.full_name)
if (
isinstance(ir.function, Function)
and ir.function.full_name == "balanceOf(address)"
@ -125,7 +138,6 @@ contract Crowdsale{
if isinstance(ir, Assignment):
if ir.rvalue in self.sources_taint:
taints.append(ir.lvalue)
return taints
# Retrieve all tainted (node, function) pairs
@ -145,7 +157,12 @@ contract Crowdsale{
for ir in node.irs_ssa:
# Filter to only tainted equality (==) comparisons
if self.is_direct_comparison(ir) and self.is_any_tainted(ir.used, taints, func):
if (
self.is_direct_comparison(ir)
# Filter out address comparisons which may occur due to lack of field sensitivity in data dependency
and self.is_not_comparing_addresses(ir)
and self.is_any_tainted(ir.used, taints, func)
):
if func not in results:
results[func] = []
results[func].append(node)

@ -6,7 +6,7 @@
"""
from typing import List
from slither.core.cfg.node import Node
from slither.core.cfg.node import Node, NodeType
from slither.core.declarations.function_contract import FunctionContract
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.utils.output import Output
@ -64,6 +64,15 @@ Bob calls `transfer`. As a result, all Ether is sent to the address `0x0` and is
self.visited_all_paths[node] = list(set(self.visited_all_paths[node] + fathers_context))
# Remove a local variable declared in a for loop header
if (
node.type == NodeType.VARIABLE
and len(node.sons) == 1 # Should always be true for a node that has a STARTLOOP son
and node.sons[0].type == NodeType.STARTLOOP
):
if node.variable_declaration in fathers_context:
fathers_context.remove(node.variable_declaration)
if self.key in node.context:
fathers_context += node.context[self.key]

@ -1363,7 +1363,14 @@ def convert_to_pop(ir: HighLevelCall, node: "Node") -> List[Operation]:
# TODO the following is equivalent to length.points_to = arr
# Should it be removed?
ir_length.lvalue.points_to = arr
element_to_delete.set_type(ElementaryType("uint256"))
# Note bytes is an ElementaryType not ArrayType so in that case we use ir.destination.type
# while in other cases such as uint256[] (ArrayType) we use ir.destination.type.type
# in this way we will have the type always set to the corresponding ElementaryType
element_to_delete.set_type(
ir.destination.type
if isinstance(ir.destination.type, ElementaryType)
else ir.destination.type.type
)
ir_assign_element_to_delete.set_expression(ir.expression)
ir_assign_element_to_delete.set_node(ir.node)
ret.append(ir_assign_element_to_delete)

@ -50,5 +50,5 @@ class Assignment(OperationWithLValue):
points = lvalue.points_to
while isinstance(points, ReferenceVariable):
points = points.points_to
return f"{lvalue} (->{points}) := {self.rvalue}({self.rvalue.type})"
return f"{lvalue}({lvalue.type}) (->{points}) := {self.rvalue}({self.rvalue.type})"
return f"{lvalue}({lvalue.type}) := {self.rvalue}({self.rvalue.type})"

@ -94,7 +94,6 @@ class BinaryType(Enum):
return self in [
BinaryType.POWER,
BinaryType.MULTIPLICATION,
BinaryType.MODULO,
BinaryType.ADDITION,
BinaryType.SUBTRACTION,
BinaryType.DIVISION,

@ -38,4 +38,7 @@ class NewArray(Call, OperationWithLValue):
def __str__(self):
args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.array_type}{'[]' * self.depth}({','.join(args)})"
lvalue = self.lvalue
return (
f"{lvalue}({lvalue.type}) = new {self.array_type}{'[]' * self.depth}({','.join(args)})"
)

@ -104,4 +104,5 @@ class NewContract(Call, OperationWithLValue): # pylint: disable=too-many-instan
if self.call_salt:
options += f"salt:{self.call_salt} "
args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.contract_name}({','.join(args)}) {options}"
lvalue = self.lvalue
return f"{lvalue}({lvalue.type}) = new {self.contract_name}({','.join(args)}) {options}"

@ -39,4 +39,5 @@ class NewStructure(Call, OperationWithLValue):
def __str__(self):
args = [str(a) for a in self.arguments]
return f"{self.lvalue} = new {self.structure_name}({','.join(args)})"
lvalue = self.lvalue
return f"{lvalue}({lvalue.type}) = new {self.structure_name}({','.join(args)})"

@ -58,7 +58,7 @@ class Unary(OperationWithLValue):
@property
def type_str(self):
return self._type.value
return str(self._type)
def __str__(self):
return f"{self.lvalue} = {self.type_str} {self.rvalue} "

@ -774,6 +774,7 @@ class FunctionSolc(CallerContextExpression):
"nodeType": "Identifier",
"src": v["src"],
"name": v["name"],
"referencedDeclaration": v["id"],
"typeDescriptions": {"typeString": v["typeDescriptions"]["typeString"]},
}
var_identifiers.append(identifier)

@ -87,6 +87,9 @@ class ModifierSolc(FunctionSolc):
for node in self._node_to_nodesolc.values():
node.analyze_expressions(self)
for yul_parser in self._node_to_yulobject.values():
yul_parser.analyze_expressions()
self._rewrite_ternary_as_if_else()
self._remove_alone_endif()

@ -15,7 +15,7 @@ ZIP_TYPES_ACCEPTED = {
Export = namedtuple("Export", ["filename", "content"])
logger = logging.getLogger("Slither")
logger = logging.getLogger("Slither-flat")
def save_to_zip(files: List[Export], zip_filename: str, zip_type: str = "lzma"):

@ -11,6 +11,7 @@ from slither.core.declarations import SolidityFunction, EnumContract, StructureC
from slither.core.declarations.contract import Contract
from slither.core.declarations.function_top_level import FunctionTopLevel
from slither.core.declarations.top_level import TopLevel
from slither.core.declarations.solidity_variables import SolidityCustomRevert
from slither.core.solidity_types import MappingType, ArrayType
from slither.core.solidity_types.type import Type
from slither.core.solidity_types.user_defined_type import UserDefinedType
@ -23,7 +24,8 @@ from slither.tools.flattening.export.export import (
save_to_disk,
)
logger = logging.getLogger("Slither-flattening")
logger = logging.getLogger("Slither-flat")
logger.setLevel(logging.INFO)
# index: where to start
# patch_type:
@ -75,6 +77,7 @@ class Flattening:
self._get_source_code_top_level(compilation_unit.structures_top_level)
self._get_source_code_top_level(compilation_unit.enums_top_level)
self._get_source_code_top_level(compilation_unit.custom_errors)
self._get_source_code_top_level(compilation_unit.variables_top_level)
self._get_source_code_top_level(compilation_unit.functions_top_level)
@ -249,12 +252,14 @@ class Flattening:
t: Type,
contract: Contract,
exported: Set[str],
list_contract: List[Contract],
list_top_level: List[TopLevel],
list_contract: Set[Contract],
list_top_level: Set[TopLevel],
):
if isinstance(t, UserDefinedType):
t_type = t.type
if isinstance(t_type, (EnumContract, StructureContract)):
if isinstance(t_type, TopLevel):
list_top_level.add(t_type)
elif isinstance(t_type, (EnumContract, StructureContract)):
if t_type.contract != contract and t_type.contract not in exported:
self._export_list_used_contracts(
t_type.contract, exported, list_contract, list_top_level
@ -275,8 +280,8 @@ class Flattening:
self,
contract: Contract,
exported: Set[str],
list_contract: List[Contract],
list_top_level: List[TopLevel],
list_contract: Set[Contract],
list_top_level: Set[TopLevel],
):
# TODO: investigate why this happen
if not isinstance(contract, Contract):
@ -332,19 +337,21 @@ class Flattening:
for read in ir.read:
if isinstance(read, TopLevel):
if read not in list_top_level:
list_top_level.append(read)
if isinstance(ir, InternalCall):
function_called = ir.function
if isinstance(function_called, FunctionTopLevel):
list_top_level.append(function_called)
if contract not in list_contract:
list_contract.append(contract)
list_top_level.add(read)
if isinstance(ir, InternalCall) and isinstance(ir.function, FunctionTopLevel):
list_top_level.add(ir.function)
if (
isinstance(ir, SolidityCall)
and isinstance(ir.function, SolidityCustomRevert)
and isinstance(ir.function.custom_error, TopLevel)
):
list_top_level.add(ir.function.custom_error)
list_contract.add(contract)
def _export_contract_with_inheritance(self, contract) -> Export:
list_contracts: List[Contract] = [] # will contain contract itself
list_top_level: List[TopLevel] = []
list_contracts: Set[Contract] = set() # will contain contract itself
list_top_level: Set[TopLevel] = set()
self._export_list_used_contracts(contract, set(), list_contracts, list_top_level)
path = Path(self._export_path, f"{contract.name}_{uuid.uuid4()}.sol")
@ -401,8 +408,8 @@ class Flattening:
def _export_with_import(self) -> List[Export]:
exports: List[Export] = []
for contract in self._compilation_unit.contracts:
list_contracts: List[Contract] = [] # will contain contract itself
list_top_level: List[TopLevel] = []
list_contracts: Set[Contract] = set() # will contain contract itself
list_top_level: Set[TopLevel] = set()
self._export_list_used_contracts(contract, set(), list_contracts, list_top_level)
if list_top_level:

@ -0,0 +1,105 @@
import argparse
import logging
from pathlib import Path
from crytic_compile import cryticparser
from slither import Slither
from slither.utils.code_generation import generate_interface
logging.basicConfig()
logger = logging.getLogger("Slither-Interface")
logger.setLevel(logging.INFO)
def parse_args() -> argparse.Namespace:
"""
Parse the underlying arguments for the program.
:return: Returns the arguments for the program.
"""
parser = argparse.ArgumentParser(
description="Generates code for a Solidity interface from contract",
usage=("slither-interface <ContractName> <source file or deployment address>"),
)
parser.add_argument(
"contract_source",
help="The name of the contract (case sensitive) followed by the deployed contract address if verified on etherscan or project directory/filename for local contracts.",
nargs="+",
)
parser.add_argument(
"--unroll-structs",
help="Whether to use structures' underlying types instead of the user-defined type",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-events",
help="Excludes event signatures in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-errors",
help="Excludes custom error signatures in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-enums",
help="Excludes enum definitions in the interface",
default=False,
action="store_true",
)
parser.add_argument(
"--exclude-structs",
help="Exclude struct definitions in the interface",
default=False,
action="store_true",
)
cryticparser.init(parser)
return parser.parse_args()
def main() -> None:
args = parse_args()
contract_name, target = args.contract_source
slither = Slither(target, **vars(args))
_contract = slither.get_contract_from_name(contract_name)[0]
interface = generate_interface(
contract=_contract,
unroll_structs=args.unroll_structs,
include_events=not args.exclude_events,
include_errors=not args.exclude_errors,
include_enums=not args.exclude_enums,
include_structs=not args.exclude_structs,
)
# add version pragma
interface = (
f"pragma solidity {_contract.compilation_unit.pragma_directives[0].version};\n\n"
+ interface
)
# write interface to file
export = Path("crytic-export", "interfaces")
export.mkdir(parents=True, exist_ok=True)
filename = f"I{contract_name}.sol"
path = Path(export, filename)
logger.info(f" Interface exported to {path}")
with open(path, "w", encoding="utf8") as f:
f.write(interface)
if __name__ == "__main__":
main()

@ -441,7 +441,7 @@ class SlitherReadStorage:
if "int" in key_type: # without this eth_utils encoding fails
key = int(key)
key = coerce_type(key_type, key)
slot = keccak(encode([key_type, "uint256"], [key, decode("uint256", slot)]))
slot = keccak(encode([key_type, "uint256"], [key, decode(["uint256"], slot)[0]]))
if isinstance(target_variable_type.type_to, UserDefinedType) and isinstance(
target_variable_type.type_to.type, Structure

@ -1,62 +1,149 @@
# Functions for generating Solidity code
from typing import TYPE_CHECKING, Optional
from slither.utils.type import convert_type_for_solidity_signature_to_string
from slither.utils.type import (
convert_type_for_solidity_signature_to_string,
export_nested_types_from_variable,
export_return_type_from_variable,
)
from slither.core.solidity_types import (
Type,
UserDefinedType,
MappingType,
ArrayType,
ElementaryType,
)
from slither.core.declarations import Structure, Enum, Contract
if TYPE_CHECKING:
from slither.core.declarations import FunctionContract, Structure, Contract
from slither.core.declarations import FunctionContract, CustomErrorContract
from slither.core.variables.state_variable import StateVariable
from slither.core.variables.local_variable import LocalVariable
def generate_interface(contract: "Contract") -> str:
# pylint: disable=too-many-arguments
def generate_interface(
contract: "Contract",
unroll_structs: bool = True,
include_events: bool = True,
include_errors: bool = True,
include_enums: bool = True,
include_structs: bool = True,
) -> str:
"""
Generates code for a Solidity interface to the contract.
Args:
contract: A Contract object
contract: A Contract object.
unroll_structs: Whether to use structures' underlying types instead of the user-defined type (default: True).
include_events: Whether to include event signatures in the interface (default: True).
include_errors: Whether to include custom error signatures in the interface (default: True).
include_enums: Whether to include enum definitions in the interface (default: True).
include_structs: Whether to include struct definitions in the interface (default: True).
Returns:
A string with the code for an interface, with function stubs for all public or external functions and
state variables, as well as any events, custom errors and/or structs declared in the contract.
"""
interface = f"interface I{contract.name} {{\n"
for event in contract.events:
name, args = event.signature
interface += f" event {name}({', '.join(args)});\n"
for error in contract.custom_errors:
args = [
convert_type_for_solidity_signature_to_string(arg.type)
.replace("(", "")
.replace(")", "")
for arg in error.parameters
]
interface += f" error {error.name}({', '.join(args)});\n"
for enum in contract.enums:
interface += f" enum {enum.name} {{ {', '.join(enum.values)} }}\n"
for struct in contract.structures:
interface += generate_struct_interface_str(struct)
if include_events:
for event in contract.events:
name, args = event.signature
interface += f" event {name}({', '.join(args)});\n"
if include_errors:
for error in contract.custom_errors:
interface += f" error {generate_custom_error_interface(error, unroll_structs)};\n"
if include_enums:
for enum in contract.enums:
interface += f" enum {enum.name} {{ {', '.join(enum.values)} }}\n"
if include_structs:
for struct in contract.structures:
interface += generate_struct_interface_str(struct, indent=4)
for var in contract.state_variables_entry_points:
interface += f" function {var.signature_str.replace('returns', 'external returns ')};\n"
interface += f" function {generate_interface_variable_signature(var, unroll_structs)};\n"
for func in contract.functions_entry_points:
if func.is_constructor or func.is_fallback or func.is_receive:
continue
interface += f" function {generate_interface_function_signature(func)};\n"
interface += (
f" function {generate_interface_function_signature(func, unroll_structs)};\n"
)
interface += "}\n\n"
return interface
def generate_interface_function_signature(func: "FunctionContract") -> Optional[str]:
def generate_interface_variable_signature(
var: "StateVariable", unroll_structs: bool = True
) -> Optional[str]:
if var.visibility in ["private", "internal"]:
return None
if unroll_structs:
params = [
convert_type_for_solidity_signature_to_string(x).replace("(", "").replace(")", "")
for x in export_nested_types_from_variable(var)
]
returns = [
convert_type_for_solidity_signature_to_string(x).replace("(", "").replace(")", "")
for x in export_return_type_from_variable(var)
]
else:
_, params, _ = var.signature
params = [p + " memory" if p in ["bytes", "string"] else p for p in params]
returns = []
_type = var.type
while isinstance(_type, MappingType):
_type = _type.type_to
while isinstance(_type, (ArrayType, UserDefinedType)):
_type = _type.type
ret = str(_type)
if isinstance(_type, Structure) or (isinstance(_type, Type) and _type.is_dynamic):
ret += " memory"
elif isinstance(_type, Contract):
ret = "address"
returns.append(ret)
return f"{var.name}({','.join(params)}) external returns ({', '.join(returns)})"
def generate_interface_function_signature(
func: "FunctionContract", unroll_structs: bool = True
) -> Optional[str]:
"""
Generates a string of the form:
func_name(type1,type2) external {payable/view/pure} returns (type3)
Args:
func: A FunctionContract object
unroll_structs: Determines whether structs are unrolled into underlying types (default: True)
Returns:
The function interface as a str (contains the return values).
Returns None if the function is private or internal, or is a constructor/fallback/receive.
"""
name, parameters, return_vars = func.signature
def format_var(var: "LocalVariable", unroll: bool) -> str:
if unroll:
return (
convert_type_for_solidity_signature_to_string(var.type)
.replace("(", "")
.replace(")", "")
)
if isinstance(var.type, ArrayType) and isinstance(
var.type.type, (UserDefinedType, ElementaryType)
):
return (
convert_type_for_solidity_signature_to_string(var.type)
.replace("(", "")
.replace(")", "")
+ f" {var.location}"
)
if isinstance(var.type, UserDefinedType):
if isinstance(var.type.type, (Structure, Enum)):
return f"{str(var.type.type)} memory"
if isinstance(var.type.type, Contract):
return "address"
if var.type.is_dynamic:
return f"{var.type} {var.location}"
return str(var.type)
name, _, _ = func.signature
if (
func not in func.contract.functions_entry_points
or func.is_constructor
@ -64,26 +151,20 @@ def generate_interface_function_signature(func: "FunctionContract") -> Optional[
or func.is_receive
):
return None
view = " view" if func.view else ""
view = " view" if func.view and not func.pure else ""
pure = " pure" if func.pure else ""
payable = " payable" if func.payable else ""
returns = [
convert_type_for_solidity_signature_to_string(ret.type).replace("(", "").replace(")", "")
for ret in func.returns
]
parameters = [
convert_type_for_solidity_signature_to_string(param.type).replace("(", "").replace(")", "")
for param in func.parameters
]
returns = [format_var(ret, unroll_structs) for ret in func.returns]
parameters = [format_var(param, unroll_structs) for param in func.parameters]
_interface_signature_str = (
name + "(" + ",".join(parameters) + ") external" + payable + pure + view
)
if len(return_vars) > 0:
if len(returns) > 0:
_interface_signature_str += " returns (" + ",".join(returns) + ")"
return _interface_signature_str
def generate_struct_interface_str(struct: "Structure") -> str:
def generate_struct_interface_str(struct: "Structure", indent: int = 0) -> str:
"""
Generates code for a structure declaration in an interface of the form:
struct struct_name {
@ -92,13 +173,37 @@ def generate_struct_interface_str(struct: "Structure") -> str:
... ...
}
Args:
struct: A Structure object
struct: A Structure object.
indent: Number of spaces to indent the code block with.
Returns:
The structure declaration code as a string.
"""
definition = f" struct {struct.name} {{\n"
spaces = ""
for _ in range(0, indent):
spaces += " "
definition = f"{spaces}struct {struct.name} {{\n"
for elem in struct.elems_ordered:
definition += f" {elem.type} {elem.name};\n"
definition += " }\n"
if isinstance(elem.type, UserDefinedType):
if isinstance(elem.type.type, (Structure, Enum)):
definition += f"{spaces} {elem.type.type} {elem.name};\n"
elif isinstance(elem.type.type, Contract):
definition += f"{spaces} address {elem.name};\n"
else:
definition += f"{spaces} {elem.type} {elem.name};\n"
definition += f"{spaces}}}\n"
return definition
def generate_custom_error_interface(
error: "CustomErrorContract", unroll_structs: bool = True
) -> str:
args = [
convert_type_for_solidity_signature_to_string(arg.type).replace("(", "").replace(")", "")
if unroll_structs
else str(arg.type.type)
if isinstance(arg.type, UserDefinedType) and isinstance(arg.type.type, (Structure, Enum))
else str(arg.type)
for arg in error.parameters
]
return f"{error.name}({', '.join(args)})"

@ -1,6 +1,6 @@
from typing import List, Dict, Union
from prettytable import PrettyTable
from prettytable.colortable import ColorTable, Themes
class MyPrettyTable:
@ -11,8 +11,8 @@ class MyPrettyTable:
def add_row(self, row: List[Union[str, List[str]]]) -> None:
self._rows.append(row)
def to_pretty_table(self) -> PrettyTable:
table = PrettyTable(self._field_names)
def to_pretty_table(self) -> ColorTable:
table = ColorTable(self._field_names, theme=Themes.OCEAN)
for row in self._rows:
table.add_row(row)
return table

@ -197,3 +197,18 @@ def export_return_type_from_variable(
return ret
return [variable_or_type.type]
def is_underlying_type_address(t: "Type") -> bool:
"""
Return true if the underlying type is an address
i.e. if the type is an address or a contract
"""
# pylint: disable=import-outside-toplevel
from slither.core.declarations.contract import Contract
if t == ElementaryType("address"):
return True
if isinstance(t, UserDefinedType) and isinstance(t.type, Contract):
return True
return False

@ -626,7 +626,6 @@ class ExpressionToSlithIR(ExpressionVisitor):
set_val(expression, value)
elif expression.type in [UnaryOperationType.MINUS_PRE]:
lvalue = TemporaryVariable(self._node)
assert isinstance(value.type, ElementaryType)
operation = Binary(lvalue, Constant("0", value.type), value, BinaryType.SUBTRACTION)
operation.set_expression(expression)
self._result.append(operation)

@ -0,0 +1,15 @@
EncodePackedCollision.bad4(bytes,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#34-36) calls abi.encodePacked() with multiple dynamic arguments:
- packed = abi.encodePacked(a,a2,a3,a) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#35)
EncodePackedCollision.bad2(string,uint256[]) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#24-26) calls abi.encodePacked() with multiple dynamic arguments:
- packed = abi.encodePacked(stra,arra) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#25)
EncodePackedCollision.bad3_get_hash_for_signature(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#29-31) calls abi.encodePacked() with multiple dynamic arguments:
- keccak256(bytes)(abi.encodePacked(name,doc)) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#30)
EncodePackedCollision.bad0(string,string) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#14-16) calls abi.encodePacked() with multiple dynamic arguments:
- packed = abi.encodePacked(stra,strb) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#15)
EncodePackedCollision.bad1(string,bytes) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#19-21) calls abi.encodePacked() with multiple dynamic arguments:
- packed = abi.encodePacked(stra,bytesa) (tests/e2e/detectors/test_data/encode-packed-collision/0.7.6/encode_packed_collision.sol#20)

@ -1,2 +1,2 @@
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-7) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5)
C.f() (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#3-8) contains an incorrect shift operation: a = 8 >> a (tests/e2e/detectors/test_data/incorrect-shift/0.7.6/shift_parameter_mixup.sol#5)

@ -1,4 +1,8 @@
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#31)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#22)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#19)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#23)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.4.25/unused_return.sol#36)

@ -1,4 +1,8 @@
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#36)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#22)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#23)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#31)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.5.16/unused_return.sol#19)

@ -1,4 +1,8 @@
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#22)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#19)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#23)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#31)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.6.11/unused_return.sol#36)

@ -1,4 +1,8 @@
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#22)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#31)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#17-29) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by a.add(0) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#23)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by t.f() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#19)
User.test(Target) (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#18-37) ignores return value by (e) = t.g() (tests/e2e/detectors/test_data/unused-return/0.7.6/unused_return.sol#36)

@ -0,0 +1,78 @@
contract ABIencodePacked{
uint a;
string str1 = "a";
string str2 = "bc";
bytes _bytes = "hello world";
uint[] arr;
uint[2] arr2;
string[3] str_arr3; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */
string[] str_array; /* This nested dynamic type is not supported in abi.encodePacked mode by solc */
bytes[] bytes_array; /* This nested dynamic type and tuples are not supported in abi.encodePacked mode by solc */
/* Two dynamic types */
function bad0(string calldata stra, string calldata strb) external{
bytes memory packed = abi.encodePacked(stra, strb);
}
/* Two dynamic types */
function bad1(string calldata stra, bytes calldata bytesa) external{
bytes memory packed = abi.encodePacked(stra, bytesa);
}
/* Two dynamic types */
function bad2(string calldata stra, uint[] calldata arra) external{
bytes memory packed = abi.encodePacked(stra, arra);
}
/* Two dynamic types */
function bad3_get_hash_for_signature(string calldata name, string calldata doc) external returns (bytes32) {
return keccak256(abi.encodePacked(name, doc));
}
/* Two dynamic types between non dynamic types */
function bad4(bytes calldata a2, bytes calldata a3) external {
bytes memory packed = abi.encodePacked(a, a2, a3, a);
}
/* Two dynamic types but static values*/
function good0() external{
bytes memory packed = abi.encodePacked(str1, str2);
}
/* Two dynamic types but static values*/
function good1() external{
bytes memory packed = abi.encodePacked(str1, _bytes);
}
/* Two dynamic types but static values*/
function good2() external{
bytes memory packed = abi.encodePacked(str1, arr);
}
/* No dynamic types */
function good3() external{
bytes memory packed = abi.encodePacked(a);
}
/* One dynamic type */
function good4() external{
bytes memory packed = abi.encodePacked(str1);
}
/* One dynamic type */
function good5() external{
bytes memory packed = abi.encodePacked(a, str1);
}
/* One dynamic type */
function good6() external{
bytes memory packed = abi.encodePacked(str1, arr2);
}
/* Two dynamic types but not consecutive*/
function good7(string calldata a, uint b, string calldata c) external{
bytes memory packed = abi.encodePacked(a, b, c);
}
}

@ -134,3 +134,27 @@ contract TestSolidityKeyword{
}
interface Receiver {
}
contract A {
mapping(address => Info) data;
struct Info {
uint a;
address b;
uint c;
}
function good(address b) public payable {
data[msg.sender] = Info(block.timestamp, b, msg.value);
if (data[msg.sender].b == address(0)) {
payable(msg.sender).transfer(data[msg.sender].c);
}
}
function good2(address b) public payable {
data[msg.sender] = Info(block.timestamp, b, msg.value);
if (Receiver(data[msg.sender].b) == Receiver(address(0))) {
payable(msg.sender).transfer(data[msg.sender].c);
}
}
}

@ -1,8 +1,9 @@
contract C {
function f() internal returns (uint a) {
function f() internal returns (uint a, uint b) {
assembly {
a := shr(a, 8)
b := shl(248, 0xff)
}
}
}

@ -6,4 +6,15 @@ contract Uninitialized{
return uint_not_init + uint_init;
}
function noreportfor() public {
for(uint i; i < 6; i++) {
uint a = i;
}
for(uint j = 0; j < 6; j++) {
uint b = j;
}
}
}

@ -6,4 +6,15 @@ contract Uninitialized{
return uint_not_init + uint_init;
}
function noreportfor() public {
for(uint i; i < 6; i++) {
uint a = i;
}
for(uint j = 0; j < 6; j++) {
uint b = j;
}
}
}

@ -6,4 +6,15 @@ contract Uninitialized{
return uint_not_init + uint_init;
}
function noreportfor() public {
for(uint i; i < 6; i++) {
uint a = i;
}
for(uint j = 0; j < 6; j++) {
uint b = j;
}
}
}

@ -6,4 +6,15 @@ contract Uninitialized{
return uint_not_init + uint_init;
}
function noreportfor() public {
for(uint i; i < 6; i++) {
uint a = i;
}
for(uint j = 0; j < 6; j++) {
uint b = j;
}
}
}

@ -8,6 +8,7 @@ library SafeMath{
contract Target{
function f() public returns(uint);
function g() public returns(uint, uint);
}
contract User{
@ -26,5 +27,12 @@ contract User{
// As the value returned by the call is stored
// (unused local variable should be another issue)
uint b = a.add(1);
t.g();
(uint c, uint d) = t.g();
// Detected as unused return
(uint e,) = t.g();
}
}

@ -8,6 +8,7 @@ library SafeMath{
contract Target{
function f() public returns(uint);
function g() public returns(uint, uint);
}
contract User{
@ -26,5 +27,12 @@ contract User{
// As the value returned by the call is stored
// (unused local variable should be another issue)
uint b = a.add(1);
t.g();
(uint c, uint d) = t.g();
// Detected as unused return
(uint e,) = t.g();
}
}

@ -8,6 +8,7 @@ library SafeMath{
abstract contract Target{
function f() public virtual returns(uint);
function g() public virtual returns(uint, uint);
}
contract User{
@ -26,5 +27,12 @@ contract User{
// As the value returned by the call is stored
// (unused local variable should be another issue)
uint b = a.add(1);
t.g();
(uint c, uint d) = t.g();
// Detected as unused return
(uint e,) = t.g();
}
}

@ -8,6 +8,7 @@ library SafeMath{
abstract contract Target{
function f() public virtual returns(uint);
function g() public virtual returns(uint, uint);
}
contract User{
@ -26,5 +27,12 @@ contract User{
// As the value returned by the call is stored
// (unused local variable should be another issue)
uint b = a.add(1);
t.g();
(uint c, uint d) = t.g();
// Detected as unused return
(uint e,) = t.g();
}
}

@ -1644,6 +1644,11 @@ ALL_TEST_OBJECTS = [
"IncorrectUsingForTopLevel.sol",
"0.8.17",
),
Test(
all_detectors.EncodePackedCollision,
"encode_packed_collision.sol",
"0.7.6",
),
]
GENERIC_PATH = "/GENERIC_PATH"

@ -452,6 +452,7 @@ ALL_TESTS = [
Test("yul-top-level-0.8.0.sol", ["0.8.0"]),
Test("complex_imports/import_aliases_issue_1319/test.sol", ["0.5.12"]),
Test("yul-state-constant-access.sol", ["0.8.16"]),
Test("negate-unary-element.sol", ["0.8.16"]),
]
# create the output folder if needed
try:

@ -1,5 +1,12 @@
contract C {
function f() public {
modifier a() {
assembly {
let y := 0
}
_;
}
function f() public a {
assembly {
let x := 0
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save