Skip to content

Instantly share code, notes, and snippets.

@0xalpharush
Created December 5, 2023 03:18
Show Gist options
  • Save 0xalpharush/b357de84c3ee138f4977362e9b54b71f to your computer and use it in GitHub Desktop.
Save 0xalpharush/b357de84c3ee138f4977362e9b54b71f to your computer and use it in GitHub Desktop.
from typing import List, Dict, Set
from slither.core.cfg.node import NodeType, Node
from slither.slithir.variables import LocalIRVariable
from slither.core.variables.local_variable import LocalVariable
from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification
from slither.slithir.operations import OperationWithLValue, Operation
class UnusedLocal(AbstractDetector):
ARGUMENT = "unused-local"
HELP = "Function assigns local variable that is does not read"
IMPACT = DetectorClassification.MEDIUM
CONFIDENCE = DetectorClassification.MEDIUM
WIKI = "https://github.com/trailofbits/slither-private/wiki/Vulnerabilities-Description#unused-local"
WIKI_TITLE = "Unused local variable"
WIKI_DESCRIPTION = (
"Function assigns local variable that is does not read which may be a logical error."
)
WIKI_EXPLOIT_SCENARIO = """
```solidity
contract Test {
uint y;
function foo(uint256 amount) public {
uint _amount = amount;
if (_amount == 0) {
_amount = 10;
}
mint(amount); // should have used _amount
}
function mint(uint256 x) internal {
y += x;
}
}
```
"""
WIKI_RECOMMENDATION = "Use the declared local variable or delete the dead code."
def gen(self, node: Node):
"""Return the set of SSA variables defined in node"""
defined = set()
for op in node.irs_ssa:
if isinstance(op, OperationWithLValue) and isinstance(op.lvalue, LocalIRVariable):
defined.add(op.lvalue)
self.non_ssa[op.lvalue] = node
return defined
def use(self, node: Node):
"""Return the set of SSA variables used in node"""
used = set()
for op in node.irs_ssa:
for read in op.read:
if isinstance(read, LocalIRVariable):
used.add(read)
return used
def merge(self, sets: List[set]):
out = set()
for s in sets:
out.update(s)
return out
def transfer(self, node: Node, out: Dict[Node, LocalIRVariable]):
"""
Return the set of {already defined but unused vars ∪ newly defined vars } - {used vars}
"""
return self.gen(node).union(out) - self.use(node)
def forward_unused_var_dataflow(self, nodes: List[Node], init: Set[LocalIRVariable]) -> Dict[Node, LocalIRVariable]:
"""
Forward dataflow analysis to find unused local variables
"""
in_ = {nodes[0]: init}
out = {node: set() for node in nodes}
worklist = nodes
while worklist:
node = worklist.pop(0)
inval = self.merge(out[n] for n in node.fathers)
in_[node] = inval
outval = self.transfer(node, inval)
# fixpoint
if outval != out[node]:
out[node] = outval
worklist += node.sons
return out
def _detect(self):
self.non_ssa: Dict[LocalIRVariable, LocalVariable] = dict()
results = []
for c in self.compilation_unit.contracts_derived:
for func in c.functions_entry_points:
if func.is_implemented:
out = self.forward_unused_var_dataflow(func.nodes, set(func.parameters_ssa))
for node, unused_vars in out.items():
# TODO merge along all paths
for var in sorted(unused_vars, key=lambda x: x.canonical_name):
info = ["The function ", func, f" defines but does not use the following variables: ", self.non_ssa[var], " .\n"]
json = self.generate_result(info)
results.append(json)
return results
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment