* Added c3-linearization-internal detector to help inheritance-graph highlight collisions via C3 linearization.

* Updated shadowing-function-internal detector to ignore unimplemented functions.
* Updated inheritance-graph printer to highlight collisions (shadowed + shadowing) instead of just shadowing.
pull/166/head
David Pokora 6 years ago
parent fa3971b6a5
commit 48bb498b14
No known key found for this signature in database
GPG Key ID: 3CED48D1BB21BDD7
  1. 0
      slither/detectors/internal/__init__.py
  2. 88
      slither/detectors/internal/c3_shadowing_internal.py
  3. 8
      slither/detectors/internal/function_shadowing_internal.py
  4. 34
      slither/printers/inheritance/inheritance_graph.py

@ -0,0 +1,88 @@
"""
Module detecting potential C3 linearization bugs (for internal use by printers)
"""
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class C3LinearizationShadowingInternal(AbstractDetector):
"""
C3 Linearization Shadowing
"""
ARGUMENT = 'shadowing-c3-internal'
HELP = 'C3 Linearization Shadowing (Internal)'
IMPACT = DetectorClassification.HIGH
CONFIDENCE = DetectorClassification.HIGH
# This detector is not meant to be called as a generic detector
# It's only used by inheritances printers
WIKI = 'undefined'
WIKI_TITLE = 'undefined'
WIKI_DESCRIPTION = 'undefined'
WIKI_EXPLOIT_SCENARIO = 'undefined'
WIKI_RECOMMENDATION = 'undefined'
def detect_shadowing_definitions(self, contract):
"""
Detects if a contract has two or more underlying contracts it inherits from which could potentially suffer from
C3 linearization shadowing bugs.
:param contract: The contract to check for potential C3 linearization shadowing within.
:return: A list of list of tuples: (contract, function), where each inner list describes colliding functions.
"""
# Loop through all contracts, and all underlying functions.
results = {}
for i in range(0, len(contract.immediate_inheritance) - 1):
inherited_contract1 = contract.immediate_inheritance[i]
for function1 in inherited_contract1.functions_and_modifiers:
# If this function has already be handled or is unimplemented, we skip it
if function1.full_name in results or function1.is_constructor or not function1.is_implemented:
continue
# Define our list of function instances which overshadow each other.
functions_matching = [(inherited_contract1, function1)]
# Loop again through other contracts and functions to compare to.
for x in range(i + 1, len(contract.immediate_inheritance)):
inherited_contract2 = contract.immediate_inheritance[x]
# Loop for each function in this contract
for function2 in inherited_contract2.functions_and_modifiers:
# Skip this function if it is the last function that was shadowed.
if function2 == functions_matching[-1][1] or function2.is_constructor or not function2.is_implemented:
continue
# If this function does have the same full name, it is shadowing through C3 linearization.
if function1.full_name == function2.full_name:
functions_matching.append((inherited_contract2, function2))
# If we have more than one definition matching the same signature, we add it to the results.
if len(functions_matching) > 1:
results[function1.full_name] = functions_matching
return list(results.values())
def detect(self):
"""
Detect shadowing as a result of C3 linearization of contracts at the same inheritance depth-level.
Recursively visit the calls
Returns:
list: {'vuln', 'filename,'contract','func', 'shadows'}
"""
results = []
for contract in self.contracts:
shadows = self.detect_shadowing_definitions(contract)
if shadows:
for shadow in shadows:
for (shadow_contract, shadow_function) in shadow:
results.append({'contract': shadow_function.contract.name,
'function': shadow_function.full_name})
return results

@ -6,15 +6,15 @@
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
class ShadowingFunctionsDetection(AbstractDetector):
class FunctionShadowingInternal(AbstractDetector):
"""
Functions shadowing detection
"""
vuln_name = "ShadowingFunctionContract"
ARGUMENT = 'shadowing-function'
HELP = 'Function Shadowing'
ARGUMENT = 'shadowing-function-internal'
HELP = 'Function Shadowing (Internal)'
IMPACT = DetectorClassification.LOW
CONFIDENCE = DetectorClassification.HIGH
@ -31,7 +31,7 @@ class ShadowingFunctionsDetection(AbstractDetector):
functions_declared = set([x.full_name for x in contract.functions_and_modifiers_not_inherited])
ret = {}
for father in contract.inheritance:
functions_declared_father = ([x.full_name for x in father.functions_and_modifiers_not_inherited])
functions_declared_father = ([x.full_name for x in father.functions_and_modifiers_not_inherited if x.is_implemented])
inter = functions_declared.intersection(functions_declared_father)
if inter:
ret[father] = inter

@ -7,7 +7,8 @@
"""
from slither.core.declarations.contract import Contract
from slither.detectors.shadowing.shadowing_functions import ShadowingFunctionsDetection
from slither.detectors.internal.function_shadowing_internal import FunctionShadowingInternal
from slither.detectors.internal.c3_shadowing_internal import C3LinearizationShadowingInternal
from slither.printers.abstract_printer import AbstractPrinter
class PrinterInheritanceGraph(AbstractPrinter):
@ -20,22 +21,37 @@ class PrinterInheritanceGraph(AbstractPrinter):
inheritance = [x.inheritance for x in slither.contracts]
self.inheritance = set([item for sublist in inheritance for item in sublist])
shadow = ShadowingFunctionsDetection(slither, None)
# Obtain functions shadowed through direct inheritance lines.
shadow = FunctionShadowingInternal(slither, None)
ret = shadow.detect()
functions_shadowed = {}
colliding_functions = {}
for s in ret:
if s['contractShadower'] not in functions_shadowed:
functions_shadowed[s['contractShadower']] = []
functions_shadowed[s['contractShadower']] += s['functions']
self.functions_shadowed = functions_shadowed
# Add to the shadowing contract.
if s['contractShadower'] not in colliding_functions:
colliding_functions[s['contractShadower']] = set()
colliding_functions[s['contractShadower']].update(s['functions'])
# Add to the shadowed contract.
if s['contract'] not in colliding_functions:
colliding_functions[s['contract']] = set()
colliding_functions[s['contract']].update(s['functions'])
# Obtain functions shadowed through c3 linearization.
shadow = C3LinearizationShadowingInternal(slither, None)
ret = shadow.detect()
for s in ret:
if s['contract'] not in colliding_functions:
colliding_functions[s['contract']] = set()
colliding_functions[s['contract']].add(s['function'])
self.colliding_functions = colliding_functions
def _get_pattern_func(self, func, contract):
# Html pattern, each line is a row in a table
func_name = func.full_name
pattern = '<TR><TD align="left"> %s</TD></TR>'
pattern_shadow = '<TR><TD align="left"><font color="#FFA500"> %s</font></TD></TR>'
if contract.name in self.functions_shadowed:
if func_name in self.functions_shadowed[contract.name]:
if contract.name in self.colliding_functions:
if func_name in self.colliding_functions[contract.name]:
return pattern_shadow % func_name
return pattern % func_name

Loading…
Cancel
Save