-
Notifications
You must be signed in to change notification settings - Fork 979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detector for encodeWithSelector wrong argument count/ mismatched types #1292
Open
feliam
wants to merge
20
commits into
dev
Choose a base branch
from
dev-wrong-encode-with-selector
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
ce84474
EncodeWhithSelector wrong params detectors - issue 227 Wip
feliam 8a79213
correct wrong encode selector
feliam ecd8adb
Added tests
feliam 677704b
remove pdb
feliam 7ce0a71
Merge branch 'dev' into dev-wrong-encode-with-selector
feliam 5fbbef5
Typo and lint
feliam 694d781
Update slither/detectors/compiler_bugs/wrong_selector_with_selector.py
feliam 23a6b59
Update slither/detectors/compiler_bugs/wrong_selector_with_selector.py
feliam 0c54b04
Update slither/detectors/compiler_bugs/wrong_selector_with_selector.py
feliam 15d79b4
Update slither/detectors/compiler_bugs/wrong_selector_with_selector.py
feliam b331206
Merge branch 'dev' into dev-wrong-encode-with-selector
tuturu-tech 3e1cb9a
Added checking of argument types if number of arguments are the same
tuturu-tech d21167e
reformatting
tuturu-tech a4794de
remove unused type
tuturu-tech f713935
Added type annotations, an array test case, and fixed typos
tuturu-tech 2127936
readding deleted snapshot
tuturu-tech b5e521d
ignore use of local selector variables
tuturu-tech cfd0b1c
remove debugging statements
tuturu-tech 3369a65
Merge branch 'dev' into dev-wrong-encode-with-selector
0xalpharush 1be3023
Merge branch 'dev' into dev-wrong-encode-with-selector
0xalpharush File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
145 changes: 145 additions & 0 deletions
145
slither/detectors/statements/wrong_encode_with_selector.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
from typing import List, Dict, Tuple | ||
from slither.core.declarations.contract import Contract | ||
from slither.core.cfg.node import Node | ||
from slither.core.declarations.function import Function | ||
from slither.slithir.operations.operation import Operation | ||
from slither.slithir.operations import TypeConversion | ||
from slither.core.expressions.literal import Literal | ||
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification | ||
from slither.slithir.operations import SolidityCall | ||
from slither.core.declarations.solidity_variables import SolidityFunction | ||
from slither.slithir.operations.assignment import Assignment | ||
from slither.slithir.variables.reference import ReferenceVariable | ||
from slither.slithir.variables.constant import Constant | ||
from slither.utils.function import get_function_id | ||
from slither.utils.output import Output | ||
|
||
|
||
def get_signatures(c: Contract) -> Dict[str, str]: | ||
"""Build a dictionary mapping function ids to signature, name, arguments for a contract""" | ||
result = {} | ||
functions = c.functions | ||
for f in functions: | ||
if f.visibility not in ["public", "external"]: | ||
continue | ||
if f.is_constructor or f.is_fallback: | ||
continue | ||
result[get_function_id(f.full_name)] = (f.full_name, *f.signature[:2]) | ||
|
||
variables = c.state_variables | ||
for variable in variables: | ||
if variable.visibility not in ["public"]: | ||
continue | ||
name = variable.full_name | ||
result[get_function_id(name)] = (name, (), ()) | ||
|
||
return result | ||
|
||
|
||
class WrongEncodeWithSelector(AbstractDetector): | ||
""" | ||
Detect calls to abi.encodeWithSelector that may result in unexpected calldata encodings | ||
""" | ||
|
||
ARGUMENT = "wrong-encode-selector" | ||
HELP = "abi.encodeWithSelector with unexpected arguments" | ||
IMPACT = DetectorClassification.MEDIUM | ||
CONFIDENCE = DetectorClassification.HIGH | ||
|
||
WIKI = "https://github.com/trailofbits/slither/wiki/encode-with-selector" | ||
WIKI_TITLE = "Parameters of incorrect type or number in abi.encodeWithSelector" | ||
WIKI_DESCRIPTION = "Wrong number or type of arguments in abi.encodeWithSelector" | ||
WIKI_EXPLOIT_SCENARIO = """ | ||
```solidity | ||
contract Test { | ||
event Val(uint, uint); | ||
function f(uint a, uint b) public { | ||
emit Val(a, b); | ||
} | ||
} | ||
contract D { | ||
function g() public { | ||
Test t = new Test(); | ||
address(t).call(abi.encodeWithSelector(Test.f.selector, "test")); | ||
} | ||
} | ||
``` | ||
abi.encodeWithSelector's arguments do not match the types expected in the function signature. | ||
function signature. | ||
""" | ||
WIKI_RECOMMENDATION = "Make sure that the type and number of arguments passed to abi.encodeWithSelector are the same as the target function signature." | ||
|
||
def _detect(self) -> List[Output]: | ||
# gather all known funcids | ||
func_ids = {} | ||
for contract in self.compilation_unit.contracts_derived: | ||
func_ids.update(get_signatures(contract)) | ||
# todo: include func_ids from the public db | ||
|
||
results = [] | ||
for contract in self.compilation_unit.contracts_derived: | ||
for func, node in check_contract(contract, func_ids): | ||
info = [ | ||
func, | ||
" calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:", | ||
node, | ||
] | ||
json = self.generate_result(info) | ||
results.append(json) | ||
|
||
return results | ||
|
||
|
||
def check_wrong_selector_encoding( | ||
function: Function, node: Node, ir: Operation, func_ids: Dict[str, str] | ||
) -> List[Tuple[Function, Node]]: | ||
result = [] | ||
if isinstance(ir, SolidityCall) and ir.function == SolidityFunction("abi.encodeWithSelector()"): | ||
# build reference bindings dict | ||
assignments = {} | ||
for ir1 in node.irs: | ||
if isinstance(ir1, Assignment): | ||
assignments[ir1.lvalue.name] = ir1.rvalue | ||
|
||
# if the selector is a reference, deref | ||
selector = ir.arguments[0] | ||
if isinstance(selector, ReferenceVariable): | ||
selector = assignments[selector.name] | ||
|
||
# Does not handle LocalVariables | ||
if not isinstance(selector, Constant): | ||
if isinstance(selector.expression, TypeConversion) and isinstance( | ||
selector.expression.expression, Literal | ||
): | ||
selector = selector.expression.expression.value | ||
else: | ||
return result | ||
|
||
_, _, argument_types = func_ids[selector.value] | ||
arguments = ir.arguments[1:] | ||
|
||
# todo: add check for user defined types | ||
if len(argument_types) != len(arguments): | ||
result.append((function, node)) | ||
else: | ||
for idx, expected in enumerate(argument_types): | ||
if expected != str(arguments[idx].type): | ||
result.append((function, node)) | ||
break | ||
|
||
return result | ||
|
||
|
||
def check_contract(contract: Contract, func_ids: Dict[str, str]) -> List[Tuple[Function, Node]]: | ||
"""Check contract's usage of abi.encodeWithSelector to ensure that the number of arguments | ||
and their type math the function signature of the given selector. | ||
""" | ||
result = [] | ||
for function in contract.functions_and_modifiers_declared: | ||
if SolidityFunction("abi.encodeWithSelector()") in function.solidity_calls: | ||
for node in function.nodes: | ||
if SolidityFunction("abi.encodeWithSelector()") in node.solidity_calls: | ||
for ir in node.irs: | ||
result += check_wrong_selector_encoding(function, node, ir, func_ids) | ||
|
||
return result |
4 changes: 4 additions & 0 deletions
4
...pshots/detectors__detector_WrongEncodeWithSelector_0_8_0_wrong_encode_selector_sol__0.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
D.bad_elementaryTypes() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#40-45) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f.selector,test,test)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#42-44) | ||
D.bad_userDefined() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#47-56) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f2.selector,badUserDefined(test,1),badUserDefined(test,1))) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#49-55) | ||
D.bad_array() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#58-64) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f3.selector,arr)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#63) | ||
D.bad_numArgs() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#35-38) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f.selector,test)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol#37) |
4 changes: 4 additions & 0 deletions
4
...shots/detectors__detector_WrongEncodeWithSelector_0_8_15_wrong_encode_selector_sol__0.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
D.bad_array() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#58-64) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f3.selector,arr)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#63) | ||
D.bad_elementaryTypes() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#40-45) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f.selector,test,test)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#42-44) | ||
D.bad_numArgs() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#35-38) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f.selector,test)) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#37) | ||
D.bad_userDefined() (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#47-56) calls abi.encodeWithSelector() with arguments of incorrect type or with an incorrect number of arguments:address(t).call(abi.encodeWithSelector(Test.f2.selector,badUserDefined(test,1),badUserDefined(test,1))) (tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol#49-55) |
78 changes: 78 additions & 0 deletions
78
tests/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
contract Test { | ||
struct UserDefined { | ||
uint256 a; | ||
string b; | ||
} | ||
|
||
event Val(uint, uint); | ||
event Val2(UserDefined, UserDefined); | ||
event Val3(uint256[]); | ||
|
||
function f(uint a, uint b) public { | ||
emit Val(a, b); | ||
} | ||
|
||
function f2(UserDefined memory a, UserDefined memory b) public { | ||
emit Val2(a, b); | ||
} | ||
|
||
function f3(uint256[] memory a) public { | ||
emit Val3(a); | ||
} | ||
} | ||
|
||
contract D { | ||
struct UserDefined { | ||
uint256 a; | ||
string b; | ||
} | ||
|
||
struct badUserDefined { | ||
string a; | ||
uint256 b; | ||
} | ||
|
||
function bad_numArgs() public { | ||
Test t = new Test(); | ||
address(t).call(abi.encodeWithSelector(Test.f.selector, "test")); | ||
} | ||
|
||
function bad_elementaryTypes() public { | ||
Test t = new Test(); | ||
address(t).call( | ||
abi.encodeWithSelector(Test.f.selector, "test", "test") | ||
); | ||
} | ||
|
||
function bad_userDefined() public { | ||
Test t = new Test(); | ||
address(t).call( | ||
abi.encodeWithSelector( | ||
Test.f2.selector, | ||
badUserDefined({a: "test", b: 1}), | ||
badUserDefined({a: "test", b: 1}) | ||
) | ||
); | ||
} | ||
|
||
function bad_array() public { | ||
Test t = new Test(); | ||
uint32[] memory arr = new uint32[](2); | ||
arr[0] = 1; | ||
arr[1] = 2; | ||
address(t).call(abi.encodeWithSelector(Test.f3.selector, arr)); | ||
} | ||
|
||
function good() public { | ||
Test t = new Test(); | ||
address(t).call(abi.encodeWithSelector(Test.f.selector, 1, 2)); | ||
} | ||
|
||
function good_array() public { | ||
Test t = new Test(); | ||
uint256[] memory arr = new uint256[](2); | ||
arr[0] = 1; | ||
arr[1] = 2; | ||
address(t).call(abi.encodeWithSelector(Test.f3.selector, arr)); | ||
} | ||
} |
Binary file added
BIN
+11.3 KB
...s/e2e/detectors/test_data/wrong-encode-selector/0.8.0/wrong-encode-selector.sol-0.8.0.zip
Binary file not shown.
78 changes: 78 additions & 0 deletions
78
tests/e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
contract Test { | ||
struct UserDefined { | ||
uint256 a; | ||
string b; | ||
} | ||
|
||
event Val(uint, uint); | ||
event Val2(UserDefined, UserDefined); | ||
event Val3(uint256[]); | ||
|
||
function f(uint a, uint b) public { | ||
emit Val(a, b); | ||
} | ||
|
||
function f2(UserDefined memory a, UserDefined memory b) public { | ||
emit Val2(a, b); | ||
} | ||
|
||
function f3(uint256[] memory a) public { | ||
emit Val3(a); | ||
} | ||
} | ||
|
||
contract D { | ||
struct UserDefined { | ||
uint256 a; | ||
string b; | ||
} | ||
|
||
struct badUserDefined { | ||
string a; | ||
uint256 b; | ||
} | ||
|
||
function bad_numArgs() public { | ||
Test t = new Test(); | ||
address(t).call(abi.encodeWithSelector(Test.f.selector, "test")); | ||
} | ||
|
||
function bad_elementaryTypes() public { | ||
Test t = new Test(); | ||
address(t).call( | ||
abi.encodeWithSelector(Test.f.selector, "test", "test") | ||
); | ||
} | ||
|
||
function bad_userDefined() public { | ||
Test t = new Test(); | ||
address(t).call( | ||
abi.encodeWithSelector( | ||
Test.f2.selector, | ||
badUserDefined({a: "test", b: 1}), | ||
badUserDefined({a: "test", b: 1}) | ||
) | ||
); | ||
} | ||
|
||
function bad_array() public { | ||
Test t = new Test(); | ||
uint32[] memory arr = new uint32[](2); | ||
arr[0] = 1; | ||
arr[1] = 2; | ||
address(t).call(abi.encodeWithSelector(Test.f3.selector, arr)); | ||
} | ||
|
||
function good() public { | ||
Test t = new Test(); | ||
address(t).call(abi.encodeWithSelector(Test.f.selector, 1, 2)); | ||
} | ||
|
||
function good_array() public { | ||
Test t = new Test(); | ||
uint256[] memory arr = new uint256[](2); | ||
arr[0] = 1; | ||
arr[1] = 2; | ||
address(t).call(abi.encodeWithSelector(Test.f3.selector, arr)); | ||
} | ||
} |
Binary file added
BIN
+12.7 KB
...e2e/detectors/test_data/wrong-encode-selector/0.8.15/wrong-encode-selector.sol-0.8.15.zip
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This causes an error on 0x9078f5996746f134f761b93939b0fd595f68845e. We should return early if we cannot determine which function the selector map backs to until we integrate a selector db