Repository: angr/angr

Issue:
`DepGraph` update is (probably) incorrect in `LiveDefinitions`
During a `ReachingDefinitionsAnalysis`, the `LiveDefinitions` state is updated depending on the definitions and uses found.
In particular, there is a `dep_graph` (for dependency graph) that is updated; this is a graph where nodes are `Definition`s, and edges represent the relation of dependency between these definitions (`a` is a successor of `b` if `a` uses `b`).

1) However, there is a **bug** in the [`_add_stack_use_by_def` method](https://github.com/angr/angr/blob/71aae4893ad77c4c696e6debefe44b5298330e30/angr/analyses/reaching_definitions/live_definitions.py#L346): an edge is added between a `Definition` and a `CodeLocation`!

2) It seems surprising that `_add_stack_use_by_def` updates the dependency graph, but `_add_register_use_by_def`, `_add_memory_use_by_def`, `_add_tmp_use_by_def` don't... Shouldn't this code (updating the graph) be moved at the end of the [_add_use_by_def method](https://github.com/angr/angr/blob/71aae4893ad77c4c696e6debefe44b5298330e30/angr/analyses/reaching_definitions/live_definitions.py#L246-L256)?


Discussion:
I agree with what you proposed. Do you want to send a PR or should I fix them?
Cool. I am on it :)

Resolving patch:
diff --git a/angr/analyses/reaching_definitions/live_definitions.py b/angr/analyses/reaching_definitions/live_definitions.py
--- a/angr/analyses/reaching_definitions/live_definitions.py
+++ b/angr/analyses/reaching_definitions/live_definitions.py
@@ -23,7 +23,7 @@ class LiveDefinitions:
 
     __slots__ = ('arch', '_subject', '_track_tmps', 'analysis', 'register_definitions', 'stack_definitions',
                  'memory_definitions', 'tmp_definitions', 'register_uses', 'stack_uses', 'memory_uses',
-                 'uses_by_codeloc', 'tmp_uses', 'all_definitions', )
+                 'uses_by_codeloc', 'tmp_uses', 'all_definitions', 'current_codeloc', 'codeloc_uses')
 
     """
     Represents the internal state of the ReachingDefinitionsAnalysis.
@@ -60,6 +60,8 @@ def __init__(self, arch, subject, track_tmps=False, analysis=None, rtoc_value=No
         self.memory_uses = Uses()
         self.uses_by_codeloc = defaultdict(set)
         self.tmp_uses = defaultdict(set)
+        self.current_codeloc = None
+        self.codeloc_uses = set()
 
     def __repr__(self):
         ctnt = "LiveDefs, %d regdefs, %d stackdefs, %d memdefs" % (
@@ -182,9 +184,9 @@ def merge(self, *others):
         return state
 
     def _cycle(self, code_loc):
-        if code_loc != self.analysis.current_codeloc:
-            self.analysis.current_codeloc = code_loc
-            self.analysis.codeloc_uses = set()
+        if code_loc != self.current_codeloc:
+            self.current_codeloc = code_loc
+            self.codeloc_uses = set()
 
     def kill_definitions(self, atom, code_loc, data=None, dummy=True):
         """
@@ -220,8 +222,7 @@ def kill_and_add_definition(self, atom, code_loc, data, dummy=False):
             self.all_definitions.add(definition)
 
             if self.dep_graph is not None:
-                self.dep_graph.add_node(definition)
-                for used in self.analysis.codeloc_uses:
+                for used in self.codeloc_uses:
                     # Moderately confusing misnomers. This is an edge from a def to a use, since the
                     # "uses" are actually the definitions that we're using and the "definition" is the
                     # new definition; i.e. The def that the old def is used to construct so this is
@@ -232,7 +233,7 @@ def kill_and_add_definition(self, atom, code_loc, data, dummy=False):
 
     def add_use(self, atom, code_loc):
         self._cycle(code_loc)
-        self.analysis.codeloc_uses.update(self.get_definitions(atom))
+        self.codeloc_uses.update(self.get_definitions(atom))
 
         if type(atom) is Register:
             self._add_register_use(atom, code_loc)
@@ -243,15 +244,18 @@ def add_use(self, atom, code_loc):
         elif type(atom) is Tmp:
             self._add_tmp_use(atom, code_loc)
 
-    def add_use_by_def(self, def_, code_loc):
-        if type(def_.atom) is Register:
-            self._add_register_use_by_def(def_, code_loc)
-        elif type(def_.atom) is SpOffset:
-            self._add_stack_use_by_def(def_, code_loc)
-        elif type(def_.atom) is MemoryLocation:
-            self._add_memory_use_by_def(def_, code_loc)
-        elif type(def_.atom) is Tmp:
-            self._add_tmp_use_by_def(def_, code_loc)
+    def add_use_by_def(self, definition, code_loc):
+        self._cycle(code_loc)
+        self.codeloc_uses.update({definition})
+
+        if type(definition.atom) is Register:
+            self._add_register_use_by_def(definition, code_loc)
+        elif type(definition.atom) is SpOffset:
+            self._add_stack_use_by_def(definition, code_loc)
+        elif type(definition.atom) is MemoryLocation:
+            self._add_memory_use_by_def(definition, code_loc)
+        elif type(definition.atom) is Tmp:
+            self._add_tmp_use_by_def(definition, code_loc)
         else:
             raise TypeError()
 
@@ -276,8 +280,7 @@ def mark_guard(self, code_loc, data, target):
         kinda_definition = Definition(atom, code_loc, data)
 
         if self.dep_graph is not None:
-            self.dep_graph.add_node(kinda_definition)
-            for used in self.analysis.codeloc_uses:
+            for used in self.codeloc_uses:
                 self.dep_graph.add_edge(used, kinda_definition)
 
     #
@@ -342,9 +345,6 @@ def _add_stack_use_by_def(self, def_, code_loc):
         self.stack_uses.add_use(def_, code_loc)
         self.uses_by_codeloc[code_loc].add(def_)
 
-        if self.dep_graph is not None:
-            self.dep_graph.add_edge(def_, code_loc)
-
     def _add_memory_use(self, atom, code_loc):
 
         # get all current definitions
diff --git a/angr/analyses/reaching_definitions/reaching_definitions.py b/angr/analyses/reaching_definitions/reaching_definitions.py
--- a/angr/analyses/reaching_definitions/reaching_definitions.py
+++ b/angr/analyses/reaching_definitions/reaching_definitions.py
@@ -83,8 +83,6 @@ def __init__(self, subject=None, func_graph=None, max_iterations=3, track_tmps=F
         self._maximum_local_call_depth = maximum_local_call_depth
 
         self._dep_graph = dep_graph
-        self.current_codeloc = None
-        self.codeloc_uses = set()
 
         if self._init_state is not None:
             self._init_state = self._init_state.copy()


Test patch: