mirror of https://github.com/crytic/slither
commit
4ee0434a84
@ -0,0 +1,114 @@ |
|||||||
|
from slither.core.declarations.solidity_variables import (SolidityFunction, |
||||||
|
SolidityVariableComposed) |
||||||
|
from slither.detectors.abstract_detector import (AbstractDetector, |
||||||
|
DetectorClassification) |
||||||
|
from slither.slithir.operations import (HighLevelCall, |
||||||
|
LowLevelCall, |
||||||
|
LibraryCall) |
||||||
|
from slither.utils.code_complexity import compute_cyclomatic_complexity |
||||||
|
|
||||||
|
|
||||||
|
class ComplexFunction(AbstractDetector): |
||||||
|
""" |
||||||
|
Module detecting complex functions |
||||||
|
A complex function is defined by: |
||||||
|
- high cyclomatic complexity |
||||||
|
- numerous writes to state variables |
||||||
|
- numerous external calls |
||||||
|
""" |
||||||
|
|
||||||
|
|
||||||
|
ARGUMENT = 'complex-function' |
||||||
|
HELP = 'Complex functions' |
||||||
|
IMPACT = DetectorClassification.INFORMATIONAL |
||||||
|
CONFIDENCE = DetectorClassification.MEDIUM |
||||||
|
|
||||||
|
MAX_STATE_VARIABLES = 10 |
||||||
|
MAX_EXTERNAL_CALLS = 5 |
||||||
|
MAX_CYCLOMATIC_COMPLEXITY = 7 |
||||||
|
|
||||||
|
CAUSE_CYCLOMATIC = "cyclomatic" |
||||||
|
CAUSE_EXTERNAL_CALL = "external_calls" |
||||||
|
CAUSE_STATE_VARS = "state_vars" |
||||||
|
|
||||||
|
|
||||||
|
@staticmethod |
||||||
|
def detect_complex_func(func): |
||||||
|
"""Detect the cyclomatic complexity of the contract functions |
||||||
|
shouldn't be greater than 7 |
||||||
|
""" |
||||||
|
result = [] |
||||||
|
code_complexity = compute_cyclomatic_complexity(func) |
||||||
|
|
||||||
|
if code_complexity > ComplexFunction.MAX_CYCLOMATIC_COMPLEXITY: |
||||||
|
result.append({ |
||||||
|
"func": func, |
||||||
|
"cause": ComplexFunction.CAUSE_CYCLOMATIC |
||||||
|
}) |
||||||
|
|
||||||
|
"""Detect the number of external calls in the func |
||||||
|
shouldn't be greater than 5 |
||||||
|
""" |
||||||
|
count = 0 |
||||||
|
for node in func.nodes: |
||||||
|
for ir in node.irs: |
||||||
|
if isinstance(ir, (HighLevelCall, LowLevelCall, LibraryCall)): |
||||||
|
count += 1 |
||||||
|
|
||||||
|
if count > ComplexFunction.MAX_EXTERNAL_CALLS: |
||||||
|
result.append({ |
||||||
|
"func": func, |
||||||
|
"cause": ComplexFunction.CAUSE_EXTERNAL_CALL |
||||||
|
}) |
||||||
|
|
||||||
|
"""Checks the number of the state variables written |
||||||
|
shouldn't be greater than 10 |
||||||
|
""" |
||||||
|
if len(func.state_variables_written) > ComplexFunction.MAX_STATE_VARIABLES: |
||||||
|
result.append({ |
||||||
|
"func": func, |
||||||
|
"cause": ComplexFunction.CAUSE_STATE_VARS |
||||||
|
}) |
||||||
|
|
||||||
|
return result |
||||||
|
|
||||||
|
def detect_complex(self, contract): |
||||||
|
ret = [] |
||||||
|
|
||||||
|
for func in contract.all_functions_called: |
||||||
|
result = self.detect_complex_func(func) |
||||||
|
ret.extend(result) |
||||||
|
|
||||||
|
return ret |
||||||
|
|
||||||
|
def detect(self): |
||||||
|
results = [] |
||||||
|
|
||||||
|
for contract in self.contracts: |
||||||
|
issues = self.detect_complex(contract) |
||||||
|
|
||||||
|
for issue in issues: |
||||||
|
func, cause = issue.values() |
||||||
|
func_name = func.name |
||||||
|
|
||||||
|
txt = "Complex function in {} Contract: {}, Function: {}" |
||||||
|
|
||||||
|
if cause == self.CAUSE_EXTERNAL_CALL: |
||||||
|
txt += ", Reason: High number of external calls" |
||||||
|
if cause == self.CAUSE_CYCLOMATIC: |
||||||
|
txt += ", Reason: High number of branches" |
||||||
|
if cause == self.CAUSE_STATE_VARS: |
||||||
|
txt += ", Reason: High number of modified state variables" |
||||||
|
|
||||||
|
info = txt.format(self.filename, |
||||||
|
contract.name, |
||||||
|
func_name) |
||||||
|
self.log(info) |
||||||
|
|
||||||
|
results.append({'vuln': 'ComplexFunc', |
||||||
|
'sourceMapping': func.source_mapping, |
||||||
|
'filename': self.filename, |
||||||
|
'contract': contract.name, |
||||||
|
'func': func_name}) |
||||||
|
return results |
||||||
|
|
@ -0,0 +1,75 @@ |
|||||||
|
# Funciton computing the code complexity |
||||||
|
|
||||||
|
def compute_number_edges(function): |
||||||
|
""" |
||||||
|
Compute the number of edges of the CFG |
||||||
|
Args: |
||||||
|
function (core.declarations.function.Function) |
||||||
|
Returns: |
||||||
|
int |
||||||
|
""" |
||||||
|
n = 0 |
||||||
|
for node in function.nodes: |
||||||
|
n += len(node.sons) |
||||||
|
return n |
||||||
|
|
||||||
|
|
||||||
|
def compute_strongly_connected_components(function): |
||||||
|
""" |
||||||
|
Compute strongly connected components |
||||||
|
Based on Kosaraju algo |
||||||
|
Implem follows wikipedia algo: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm#The_algorithm |
||||||
|
Args: |
||||||
|
function (core.declarations.function.Function) |
||||||
|
Returns: |
||||||
|
list(list(nodes)) |
||||||
|
""" |
||||||
|
visited = {n:False for n in function.nodes} |
||||||
|
assigned = {n:False for n in function.nodes} |
||||||
|
components = [] |
||||||
|
l = [] |
||||||
|
|
||||||
|
def visit(node): |
||||||
|
if not visited[node]: |
||||||
|
visited[node] = True |
||||||
|
for son in node.sons: |
||||||
|
visit(son) |
||||||
|
l.append(node) |
||||||
|
|
||||||
|
for n in function.nodes: |
||||||
|
visit(n) |
||||||
|
|
||||||
|
def assign(node, root): |
||||||
|
if not assigned[node]: |
||||||
|
assigned[node] = True |
||||||
|
root.append(node) |
||||||
|
for father in node.fathers: |
||||||
|
assign(father, root) |
||||||
|
|
||||||
|
for n in l: |
||||||
|
component = [] |
||||||
|
assign(n, component) |
||||||
|
if component: |
||||||
|
components.append(component) |
||||||
|
|
||||||
|
return components |
||||||
|
|
||||||
|
def compute_cyclomatic_complexity(function): |
||||||
|
""" |
||||||
|
Compute the cyclomatic complexity of a function |
||||||
|
Args: |
||||||
|
function (core.declarations.function.Function) |
||||||
|
Returns: |
||||||
|
int |
||||||
|
""" |
||||||
|
# from https://en.wikipedia.org/wiki/Cyclomatic_complexity |
||||||
|
# M = E - N + 2P |
||||||
|
# where M is the complexity |
||||||
|
# E number of edges |
||||||
|
# N number of nodes |
||||||
|
# P number of connected components |
||||||
|
|
||||||
|
E = compute_number_edges(function) |
||||||
|
N = len(function.nodes) |
||||||
|
P = len(compute_strongly_connected_components(function)) |
||||||
|
return E - N + 2 * P |
@ -0,0 +1,88 @@ |
|||||||
|
pragma solidity ^0.4.24; |
||||||
|
|
||||||
|
contract Complex { |
||||||
|
int numberOfSides = 7; |
||||||
|
string shape; |
||||||
|
uint i0 = 0; |
||||||
|
uint i1 = 0; |
||||||
|
uint i2 = 0; |
||||||
|
uint i3 = 0; |
||||||
|
uint i4 = 0; |
||||||
|
uint i5 = 0; |
||||||
|
uint i6 = 0; |
||||||
|
uint i7 = 0; |
||||||
|
uint i8 = 0; |
||||||
|
uint i9 = 0; |
||||||
|
uint i10 = 0; |
||||||
|
|
||||||
|
|
||||||
|
function computeShape() external { |
||||||
|
if (numberOfSides <= 2) { |
||||||
|
shape = "Cant be a shape!"; |
||||||
|
} else if (numberOfSides == 3) { |
||||||
|
shape = "Triangle"; |
||||||
|
} else if (numberOfSides == 4) { |
||||||
|
shape = "Square"; |
||||||
|
} else if (numberOfSides == 5) { |
||||||
|
shape = "Pentagon"; |
||||||
|
} else if (numberOfSides == 6) { |
||||||
|
shape = "Hexagon"; |
||||||
|
} else if (numberOfSides == 7) { |
||||||
|
shape = "Heptagon"; |
||||||
|
} else if (numberOfSides == 8) { |
||||||
|
shape = "Octagon"; |
||||||
|
} else if (numberOfSides == 9) { |
||||||
|
shape = "Nonagon"; |
||||||
|
} else if (numberOfSides == 10) { |
||||||
|
shape = "Decagon"; |
||||||
|
} else if (numberOfSides == 11) { |
||||||
|
shape = "Hendecagon"; |
||||||
|
} else { |
||||||
|
shape = "Your shape is more than 11 sides."; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function complexExternalWrites() external { |
||||||
|
Increment test1 = new Increment(); |
||||||
|
test1.increaseBy1(); |
||||||
|
test1.increaseBy1(); |
||||||
|
test1.increaseBy1(); |
||||||
|
test1.increaseBy1(); |
||||||
|
test1.increaseBy1(); |
||||||
|
|
||||||
|
Increment test2 = new Increment(); |
||||||
|
test2.increaseBy1(); |
||||||
|
|
||||||
|
address test3 = new Increment(); |
||||||
|
test3.call(bytes4(keccak256("increaseBy2()"))); |
||||||
|
|
||||||
|
address test4 = new Increment(); |
||||||
|
test4.call(bytes4(keccak256("increaseBy2()"))); |
||||||
|
} |
||||||
|
|
||||||
|
function complexStateVars() external { |
||||||
|
i0 = 1; |
||||||
|
i1 = 1; |
||||||
|
i2 = 1; |
||||||
|
i3 = 1; |
||||||
|
i4 = 1; |
||||||
|
i5 = 1; |
||||||
|
i6 = 1; |
||||||
|
i7 = 1; |
||||||
|
i8 = 1; |
||||||
|
i9 = 1; |
||||||
|
i10 = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
contract Increment { |
||||||
|
uint i = 0; |
||||||
|
|
||||||
|
function increaseBy1() public { |
||||||
|
i += 1; |
||||||
|
} |
||||||
|
|
||||||
|
function increaseBy2() public { |
||||||
|
i += 2; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue