Created
December 5, 2023 03:18
-
-
Save 0xalpharush/b357de84c3ee138f4977362e9b54b71f to your computer and use it in GitHub Desktop.
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
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