mirror of https://github.com/crytic/slither
parent
1d278f520c
commit
52322ebb37
@ -0,0 +1,133 @@ |
|||||||
|
""" |
||||||
|
Module detecting reserved keyword shadowing |
||||||
|
""" |
||||||
|
|
||||||
|
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||||
|
|
||||||
|
|
||||||
|
class BuiltinSymbolShadowing(AbstractDetector): |
||||||
|
""" |
||||||
|
Built-in symbol shadowing |
||||||
|
""" |
||||||
|
|
||||||
|
ARGUMENT = 'shadowing-builtin' |
||||||
|
HELP = 'Built-in symbol shadowing' |
||||||
|
IMPACT = DetectorClassification.LOW |
||||||
|
CONFIDENCE = DetectorClassification.HIGH |
||||||
|
|
||||||
|
WIKI = 'https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#builtin-symbol-shadowing' |
||||||
|
|
||||||
|
vuln_name = 'ShadowingBuiltinSymbols' |
||||||
|
|
||||||
|
SHADOWING_FUNCTION = "function" |
||||||
|
SHADOWING_MODIFIER = "modifier" |
||||||
|
SHADOWING_LOCAL_VARIABLE = "local variable" |
||||||
|
SHADOWING_STATE_VARIABLE = "state variable" |
||||||
|
SHADOWING_EVENT = "event" |
||||||
|
|
||||||
|
# Reserved keywords reference: https://solidity.readthedocs.io/en/v0.5.2/units-and-global-variables.html |
||||||
|
BUILTIN_SYMBOLS = ["assert", "require", "revert", "block", "blockhash", |
||||||
|
"gasleft", "msg", "now", "tx", "this", "addmod", "mulmod", |
||||||
|
"keccak256", "sha256", "sha3", "ripemd160", "ecrecover", |
||||||
|
"selfdestruct", "suicide", "abi"] |
||||||
|
|
||||||
|
# https://solidity.readthedocs.io/en/v0.5.2/miscellaneous.html#reserved-keywords |
||||||
|
RESERVED_KEYWORDS = ['abstract', 'after', 'alias', 'apply', 'auto', 'case', 'catch', 'copyof', |
||||||
|
'default', 'define', 'final', 'immutable', 'implements', 'in', 'inline', |
||||||
|
'let', 'macro', 'match', 'mutable', 'null', 'of', 'override', 'partial', |
||||||
|
'promise', 'reference', 'relocatable', 'sealed', 'sizeof', 'static', |
||||||
|
'supports', 'switch', 'try', 'type', 'typedef', 'typeof', 'unchecked'] |
||||||
|
|
||||||
|
def is_builtin_symbol(self, word): |
||||||
|
""" Detects if a given word is a built-in symbol. |
||||||
|
|
||||||
|
Returns: |
||||||
|
boolean: True if the given word represents a built-in symbol.""" |
||||||
|
|
||||||
|
return word in self.BUILTIN_SYMBOLS or word in self.RESERVED_KEYWORDS |
||||||
|
|
||||||
|
def detect_builtin_shadowing_locals(self, function_or_modifier): |
||||||
|
""" Detects if local variables in a given function/modifier are named after built-in symbols. |
||||||
|
Any such items are returned in a list. |
||||||
|
|
||||||
|
Returns: |
||||||
|
list of tuple: (type, definition, local variable parent)""" |
||||||
|
|
||||||
|
results = [] |
||||||
|
for local in function_or_modifier.variables: |
||||||
|
if self.is_builtin_symbol(local.name): |
||||||
|
results.append((self.SHADOWING_LOCAL_VARIABLE, local, function_or_modifier)) |
||||||
|
return results |
||||||
|
|
||||||
|
def detect_builtin_shadowing_definitions(self, contract): |
||||||
|
""" Detects if functions, access modifiers, events, state variables, or local variables are named after built-in |
||||||
|
symbols. Any such definitions are returned in a list. |
||||||
|
|
||||||
|
Returns: |
||||||
|
list of tuple: (type, definition, [local variable parent])""" |
||||||
|
|
||||||
|
result = [] |
||||||
|
|
||||||
|
# Loop through all functions, modifiers, variables (state and local) to detect any built-in symbol keywords. |
||||||
|
for function in contract.functions: |
||||||
|
if function.contract == contract: |
||||||
|
if self.is_builtin_symbol(function.name): |
||||||
|
result.append((self.SHADOWING_FUNCTION, function, None)) |
||||||
|
result += self.detect_builtin_shadowing_locals(function) |
||||||
|
for modifier in contract.modifiers: |
||||||
|
if modifier.contract == contract: |
||||||
|
if self.is_builtin_symbol(modifier.name): |
||||||
|
result.append((self.SHADOWING_MODIFIER, modifier, None)) |
||||||
|
result += self.detect_builtin_shadowing_locals(modifier) |
||||||
|
for variable in contract.variables: |
||||||
|
if variable.contract == contract: |
||||||
|
if self.is_builtin_symbol(variable.name): |
||||||
|
result.append((self.SHADOWING_STATE_VARIABLE, variable, None)) |
||||||
|
for event in contract.events: |
||||||
|
if event.contract == contract: |
||||||
|
if self.is_builtin_symbol(event.name): |
||||||
|
result.append((self.SHADOWING_EVENT, event, None)) |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def detect(self): |
||||||
|
""" Detect shadowing of built-in symbols |
||||||
|
|
||||||
|
Recursively visit the calls |
||||||
|
Returns: |
||||||
|
list: {'vuln', 'filename,'contract','func', 'shadow'} |
||||||
|
|
||||||
|
""" |
||||||
|
|
||||||
|
results = [] |
||||||
|
for contract in self.contracts: |
||||||
|
shadows = self.detect_builtin_shadowing_definitions(contract) |
||||||
|
if shadows: |
||||||
|
for shadow in shadows: |
||||||
|
# Obtain components |
||||||
|
shadow_type = shadow[0] |
||||||
|
shadow_object = shadow[1] |
||||||
|
local_variable_parent = shadow[2] |
||||||
|
|
||||||
|
# Build the path for our info string |
||||||
|
local_variable_path = contract.name + "." |
||||||
|
if local_variable_parent is not None: |
||||||
|
local_variable_path += local_variable_parent.name + "." |
||||||
|
local_variable_path += shadow_object.name |
||||||
|
|
||||||
|
info = '{} ({} @ {}) shadows built-in symbol \"{}"\n'.format(local_variable_path, |
||||||
|
shadow_type, |
||||||
|
shadow_object.source_mapping_str, |
||||||
|
shadow_object.name) |
||||||
|
# Print relevant information to the log |
||||||
|
self.log(info) |
||||||
|
|
||||||
|
# Generate relevant JSON data for this shadowing definition. |
||||||
|
json = self.generate_json_result(info) |
||||||
|
if shadow_type in [self.SHADOWING_FUNCTION, self.SHADOWING_MODIFIER, self.SHADOWING_EVENT]: |
||||||
|
self.add_function_to_json(shadow_object, json) |
||||||
|
elif shadow_type in [self.SHADOWING_STATE_VARIABLE, self.SHADOWING_LOCAL_VARIABLE]: |
||||||
|
self.add_variable_to_json(shadow_object, json) |
||||||
|
results.append(json) |
||||||
|
|
||||||
|
return results |
@ -0,0 +1,109 @@ |
|||||||
|
""" |
||||||
|
Module detecting local variable shadowing |
||||||
|
""" |
||||||
|
|
||||||
|
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification |
||||||
|
|
||||||
|
|
||||||
|
class LocalShadowing(AbstractDetector): |
||||||
|
""" |
||||||
|
Local variable shadowing |
||||||
|
""" |
||||||
|
|
||||||
|
ARGUMENT = 'shadowing-local' |
||||||
|
HELP = 'Local variables shadowing' |
||||||
|
IMPACT = DetectorClassification.LOW |
||||||
|
CONFIDENCE = DetectorClassification.HIGH |
||||||
|
|
||||||
|
WIKI = 'https://github.com/trailofbits/slither/wiki/Vulnerabilities-Description#local-variable-shadowing' |
||||||
|
|
||||||
|
vuln_name = 'ShadowingLocalVariable' |
||||||
|
|
||||||
|
OVERSHADOWED_FUNCTION = "function" |
||||||
|
OVERSHADOWED_MODIFIER = "modifier" |
||||||
|
OVERSHADOWED_STATE_VARIABLE = "state variable" |
||||||
|
OVERSHADOWED_EVENT = "event" |
||||||
|
|
||||||
|
def detect_shadowing_definitions(self, contract): |
||||||
|
""" Detects if functions, access modifiers, events, state variables, and local variables are named after |
||||||
|
reserved keywords. Any such definitions are returned in a list. |
||||||
|
|
||||||
|
Returns: |
||||||
|
list of tuple: (type, contract name, definition)""" |
||||||
|
result = [] |
||||||
|
|
||||||
|
# Loop through all functions + modifiers in this contract. |
||||||
|
for function in contract.functions + contract.modifiers: |
||||||
|
# We should only look for functions declared directly in this contract (not in a base contract). |
||||||
|
if function.contract != contract: |
||||||
|
continue |
||||||
|
|
||||||
|
# This function was declared in this contract, we check what its local variables might shadow. |
||||||
|
for variable in function.variables: |
||||||
|
overshadowed = [] |
||||||
|
for scope_contract in [contract] + contract.inheritance: |
||||||
|
# Check functions |
||||||
|
for scope_function in scope_contract.functions: |
||||||
|
if variable.name == scope_function.name and scope_function.contract == scope_contract: |
||||||
|
overshadowed.append((self.OVERSHADOWED_FUNCTION, scope_contract.name, scope_function)) |
||||||
|
# Check modifiers |
||||||
|
for scope_modifier in scope_contract.modifiers: |
||||||
|
if variable.name == scope_modifier.name and scope_modifier.contract == scope_contract: |
||||||
|
overshadowed.append((self.OVERSHADOWED_MODIFIER, scope_contract.name, scope_modifier)) |
||||||
|
# Check events |
||||||
|
for scope_event in scope_contract.events: |
||||||
|
if variable.name == scope_event.name and scope_event.contract == scope_contract: |
||||||
|
overshadowed.append((self.OVERSHADOWED_EVENT, scope_contract.name, scope_event)) |
||||||
|
# Check state variables |
||||||
|
for scope_state_variable in scope_contract.variables: |
||||||
|
if variable.name == scope_state_variable.name and scope_state_variable.contract == scope_contract: |
||||||
|
overshadowed.append((self.OVERSHADOWED_STATE_VARIABLE, scope_contract.name, scope_state_variable)) |
||||||
|
|
||||||
|
# If we have found any overshadowed objects, we'll want to add it to our result list. |
||||||
|
if overshadowed: |
||||||
|
result.append((contract.name, function.name, variable, overshadowed)) |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def detect(self): |
||||||
|
""" Detect shadowing local variables |
||||||
|
|
||||||
|
Recursively visit the calls |
||||||
|
Returns: |
||||||
|
list: {'vuln', 'filename,'contract','func', 'shadow'} |
||||||
|
|
||||||
|
""" |
||||||
|
|
||||||
|
results = [] |
||||||
|
for contract in self.contracts: |
||||||
|
shadows = self.detect_shadowing_definitions(contract) |
||||||
|
if shadows: |
||||||
|
for shadow in shadows: |
||||||
|
local_parent_name = shadow[1] |
||||||
|
local_variable = shadow[2] |
||||||
|
overshadowed = shadow[3] |
||||||
|
info = '{}.{}.{} (local variable @ {}) shadows:\n'.format(contract.name, |
||||||
|
local_parent_name, |
||||||
|
local_variable.name, |
||||||
|
local_variable.source_mapping_str) |
||||||
|
for overshadowed_entry in overshadowed: |
||||||
|
info += "\t- {}.{} ({} @ {})\n".format(overshadowed_entry[1], |
||||||
|
overshadowed_entry[2], |
||||||
|
overshadowed_entry[0], |
||||||
|
overshadowed_entry[2].source_mapping_str) |
||||||
|
|
||||||
|
# Print relevant information to the log |
||||||
|
self.log(info) |
||||||
|
|
||||||
|
# Generate relevant JSON data for this shadowing definition. |
||||||
|
json = self.generate_json_result(info) |
||||||
|
self.add_variable_to_json(local_variable, json) |
||||||
|
for overshadowed_entry in overshadowed: |
||||||
|
if overshadowed_entry[0] in [self.OVERSHADOWED_FUNCTION, self.OVERSHADOWED_MODIFIER, |
||||||
|
self.OVERSHADOWED_EVENT]: |
||||||
|
self.add_function_to_json(overshadowed_entry[2], json) |
||||||
|
elif overshadowed_entry[0] == self.OVERSHADOWED_STATE_VARIABLE: |
||||||
|
self.add_variable_to_json(overshadowed_entry[2], json) |
||||||
|
results.append(json) |
||||||
|
|
||||||
|
return results |
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@ |
|||||||
|
[{"check": "shadowing-local", "impact": "Low", "confidence": "High", "description": "FurtherExtendedContract.shadowingParent.x (local variable @ tests/shadowing_local_variable.sol#25) shadows:\n\t- FurtherExtendedContract.x (state variable @ tests/shadowing_local_variable.sol#17)\n\t- ExtendedContract.x (state variable @ tests/shadowing_local_variable.sol#9)\n\t- BaseContract.x (state variable @ tests/shadowing_local_variable.sol#4)\n", "elements": [{"type": "variable", "name": "x", "source_mapping": {"start": 376, "length": 6, "filename": "tests/shadowing_local_variable.sol", "lines": [25]}}, {"type": "variable", "name": "x", "source_mapping": {"start": 256, "length": 10, "filename": "tests/shadowing_local_variable.sol", "lines": [17]}}, {"type": "variable", "name": "x", "source_mapping": {"start": 133, "length": 10, "filename": "tests/shadowing_local_variable.sol", "lines": [9]}}, {"type": "variable", "name": "x", "source_mapping": {"start": 54, "length": 10, "filename": "tests/shadowing_local_variable.sol", "lines": [4]}}]}, {"check": "shadowing-local", "impact": "Low", "confidence": "High", "description": "FurtherExtendedContract.shadowingParent.y (local variable @ tests/shadowing_local_variable.sol#25) shadows:\n\t- BaseContract.y (state variable @ tests/shadowing_local_variable.sol#5)\n", "elements": [{"type": "variable", "name": "y", "source_mapping": {"start": 398, "length": 5, "filename": "tests/shadowing_local_variable.sol", "lines": [25]}}, {"type": "variable", "name": "y", "source_mapping": {"start": 70, "length": 10, "filename": "tests/shadowing_local_variable.sol", "lines": [5]}}]}, {"check": "shadowing-local", "impact": "Low", "confidence": "High", "description": "FurtherExtendedContract.shadowingParent.z (local variable @ tests/shadowing_local_variable.sol#25) shadows:\n\t- ExtendedContract.z (function @ tests/shadowing_local_variable.sol#11)\n", "elements": [{"type": "variable", "name": "z", "source_mapping": {"start": 405, "length": 6, "filename": "tests/shadowing_local_variable.sol", "lines": [25]}}, {"type": "function", "name": "z", "source_mapping": {"start": 150, "length": 27, "filename": "tests/shadowing_local_variable.sol", "lines": [11]}, "contract": {"type": "contract", "name": "ExtendedContract", "source_mapping": {"start": 85, "length": 110, "filename": "tests/shadowing_local_variable.sol", "lines": [8, 9, 10, 11, 12, 13, 14]}}}]}, {"check": "shadowing-local", "impact": "Low", "confidence": "High", "description": "FurtherExtendedContract.shadowingParent.w (local variable @ tests/shadowing_local_variable.sol#25) shadows:\n\t- FurtherExtendedContract.w (modifier @ tests/shadowing_local_variable.sol#20-23)\n", "elements": [{"type": "variable", "name": "w", "source_mapping": {"start": 413, "length": 6, "filename": "tests/shadowing_local_variable.sol", "lines": [25]}}, {"type": "function", "name": "w", "source_mapping": {"start": 274, "length": 71, "filename": "tests/shadowing_local_variable.sol", "lines": [20, 21, 22, 23]}, "contract": {"type": "contract", "name": "FurtherExtendedContract", "source_mapping": {"start": 197, "length": 235, "filename": "tests/shadowing_local_variable.sol", "lines": [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]}}}]}, {"check": "shadowing-local", "impact": "Low", "confidence": "High", "description": "FurtherExtendedContract.shadowingParent.v (local variable @ tests/shadowing_local_variable.sol#25) shadows:\n\t- ExtendedContract.v (event @ tests/shadowing_local_variable.sol#13)\n", "elements": [{"type": "variable", "name": "v", "source_mapping": {"start": 421, "length": 6, "filename": "tests/shadowing_local_variable.sol", "lines": [25]}}, {"type": "function", "name": "v", "source_mapping": {"start": 183, "length": 10, "filename": "tests/shadowing_local_variable.sol", "lines": [13]}, "contract": {"type": "contract", "name": "ExtendedContract", "source_mapping": {"start": 85, "length": 110, "filename": "tests/shadowing_local_variable.sol", "lines": [8, 9, 10, 11, 12, 13, 14]}}}]}] |
@ -0,0 +1,34 @@ |
|||||||
|
pragma solidity ^0.4.25; |
||||||
|
|
||||||
|
contract BaseContract { |
||||||
|
uint blockhash; |
||||||
|
uint now; |
||||||
|
|
||||||
|
event revert(bool condition); |
||||||
|
} |
||||||
|
|
||||||
|
contract ExtendedContract is BaseContract { |
||||||
|
uint ecrecover = 7; |
||||||
|
|
||||||
|
function assert(bool condition) public { |
||||||
|
uint msg; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract FurtherExtendedContract is ExtendedContract { |
||||||
|
uint blockhash = 7; |
||||||
|
uint this = 5; |
||||||
|
uint abi; |
||||||
|
|
||||||
|
modifier require { |
||||||
|
assert(msg.sender != address(0)); |
||||||
|
uint keccak256; |
||||||
|
uint sha3; |
||||||
|
_; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract Reserved{ |
||||||
|
address mutable; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
pragma solidity ^0.4.24; |
||||||
|
|
||||||
|
contract BaseContract { |
||||||
|
uint x = 5; |
||||||
|
uint y = 5; |
||||||
|
} |
||||||
|
|
||||||
|
contract ExtendedContract is BaseContract { |
||||||
|
uint x = 7; |
||||||
|
|
||||||
|
function z() public pure {} |
||||||
|
|
||||||
|
event v(); |
||||||
|
} |
||||||
|
|
||||||
|
contract FurtherExtendedContract is ExtendedContract { |
||||||
|
uint x = 7; |
||||||
|
|
||||||
|
|
||||||
|
modifier w { |
||||||
|
assert(msg.sender != address(0)); |
||||||
|
_; |
||||||
|
} |
||||||
|
|
||||||
|
function shadowingParent(uint x) public pure { int y; uint z; uint w; uint v; } |
||||||
|
} |
Loading…
Reference in new issue