Repository: crytic/slither

Issue:
Slither crashes with assert v == variable (block scoping?)
I tried running the tool, but I get the following output:

```
  File "/usr/local/lib/python3.8/site-packages/slither/__main__.py", line 692, in main_impl
    ) = process_all(filename, args, detector_classes, printer_classes)
  File "/usr/local/lib/python3.8/site-packages/slither/__main__.py", line 79, in process_all
    ) = process_single(compilation, args, detector_classes, printer_classes)
  File "/usr/local/lib/python3.8/site-packages/slither/__main__.py", line 62, in process_single
    slither = Slither(target, ast_format=ast, **vars(args))
  File "/usr/local/lib/python3.8/site-packages/slither/slither.py", line 100, in __init__
    self._parser.analyze_contracts()
  File "/usr/local/lib/python3.8/site-packages/slither/solc_parsing/slitherSolc.py", line 353, in analyze_contracts
    self._convert_to_slithir()
  File "/usr/local/lib/python3.8/site-packages/slither/solc_parsing/slitherSolc.py", line 508, in _convert_to_slithir
    contract.convert_expression_to_slithir_ssa()
  File "/usr/local/lib/python3.8/site-packages/slither/core/declarations/contract.py", line 1185, in convert_expression_to_slithir_ssa
    func.generate_slithir_ssa(all_ssa_state_variables_instances)
  File "/usr/local/lib/python3.8/site-packages/slither/core/declarations/function.py", line 1677, in generate_slithir_ssa
    add_ssa_ir(self, all_ssa_state_variables_instances)
  File "/usr/local/lib/python3.8/site-packages/slither/slithir/utils/ssa.py", line 130, in add_ssa_ir
    add_phi_origins(function.entry_point, init_definition, dict())
  File "/usr/local/lib/python3.8/site-packages/slither/slithir/utils/ssa.py", line 541, in add_phi_origins
    add_phi_origins(succ, local_variables_definition, state_variables_definition)
  File "/usr/local/lib/python3.8/site-packages/slither/slithir/utils/ssa.py", line 541, in add_phi_origins
    add_phi_origins(succ, local_variables_definition, state_variables_definition)
  File "/usr/local/lib/python3.8/site-packages/slither/slithir/utils/ssa.py", line 541, in add_phi_origins
    add_phi_origins(succ, local_variables_definition, state_variables_definition)
  [Previous line repeated 11 more times]
  File "/usr/local/lib/python3.8/site-packages/slither/slithir/utils/ssa.py", line 534, in add_phi_origins
    phi_node.add_phi_origin_local_variable(variable, n)
  File "/usr/local/lib/python3.8/site-packages/slither/core/cfg/node.py", line 820, in add_phi_origin_local_variable
    assert v == variable
AssertionError
```

Mac OS: 10.15.6
Python: 3.8.5
slither: 0.6.13
solc: 0.6.12

I can see similar errors in https://github.com/crytic/slither/issues/151 and https://github.com/crytic/slither/issues/534. And it seems to have something to do with block scoping. The contracts do in fact use block scoping in several places to avoid stack too deep errors. Unfortunately it's a closed source project, so I cannot share all code.

But if you have any suggestions, I can look into it more.


Discussion:

Resolving patch:
diff --git a/slither/solc_parsing/yul/parse_yul.py b/slither/solc_parsing/yul/parse_yul.py
--- a/slither/solc_parsing/yul/parse_yul.py
+++ b/slither/solc_parsing/yul/parse_yul.py
@@ -89,6 +89,21 @@ def link_underlying_nodes(node1: YulNode, node2: YulNode):
     link_nodes(node1.underlying_node, node2.underlying_node)
 
 
+def _name_to_yul_name(variable_name: str, yul_id: List[str]) -> str:
+    """
+    Translate the variable name to a unique yul name
+    Within the same function, yul blocks can declare
+    different variables with the same name
+    We need to create unique name per variable
+    to prevent collision during the SSA generation
+
+    :param var:
+    :param yul_id:
+    :return:
+    """
+    return variable_name + f"_{'_'.join(yul_id)}"
+
+
 class YulScope(metaclass=abc.ABCMeta):
     __slots__ = [
         "_contract",
@@ -135,7 +150,11 @@ def add_yul_local_variable(self, var):
 
     def get_yul_local_variable_from_name(self, variable_name):
         return next(
-            (v for v in self._yul_local_variables if v.underlying.name == variable_name),
+            (
+                v
+                for v in self._yul_local_variables
+                if v.underlying.name == _name_to_yul_name(variable_name, self.id)
+            ),
             None,
         )
 
@@ -162,7 +181,7 @@ def __init__(self, var: LocalVariable, root: YulScope, ast: Dict):
         var.set_function(root.function)
         var.set_offset(ast["src"], root.slither)
 
-        var.name = ast["name"]
+        var.name = _name_to_yul_name(ast["name"], root.id)
         var.set_type(ElementaryType("uint256"))
         var.set_location("memory")
 
@@ -516,7 +535,6 @@ def convert_yul_typed_name(root: YulScope, parent: YulNode, ast: Dict) -> YulNod
     local_var = LocalVariable()
 
     var = YulLocalVariable(local_var, root, ast)
-
     root.add_yul_local_variable(var)
 
     node = root.new_node(NodeType.VARIABLE, ast["src"])


Test patch: