Repository: angr/angr

Issue:
Cfg incorrectly recovered?
### Description

Decompiling function "argmatch_to_argument", whose source code is as [followed](https://github.com/coreutils/gnulib/blob/08ba9aaebff69a02cbb794c6213314fd09dd5ec5/lib/argmatch.c#L216):
```
const char *
argmatch_to_argument (const void *value,
                      const char *const *arglist,
                      const void *vallist, size_t valsize)
{
  size_t i;

  for (i = 0; arglist[i]; i++)

    if (!memcmp (value, (char const *) vallist + valsize * i, valsize))
      return arglist[i];
  return NULL;
}
```
angr gives me the following decompiled code:
```
int argmatch_to_argument(void *a0, unsigned long long *a1, void *a2, unsigned int a3)
{
    unsigned long long v1;  // r13
    unsigned long long *v3;  // rbx
    unsigned long long v4;  // rax
    void *v5;  // r15
    unsigned long long v6;  // rax

    v1 = *(a1);
    if (!(*(a1) != 0))
    {
        v1 = 0;
        v6 = v1;
        return v6;
    }
    else if (bcmp(a0, a2, a3) != 0)
    {
        v5 = a2 + a3;
        v3 = &a1[1];
        while (true)
        {
            v1 = *(v3);
            if (*(v3) != 0)
            {
                v4 = bcmp(a0, v5, a3);
                v5 += a3;
                v3 = &v3[1];
                v6 = v1;
                return v6;
                v1 = 0;
                v6 = v1;
                return v6;
            }
        }
    }
    else
    {
        v6 = v1;
        return v6;
    }
}
```
Note that there are two `return v6;` in the `while(true)` loop. And it looks like the value of ` v4 = bcmp(a0, v5, a3);`, which correspond to ` memcmp (value, (char const *) vallist + valsize * i, valsize)` in the source code, is not used in the `while(true)` loop.
the disassembled code:
![123](https://user-images.githubusercontent.com/54459497/210153110-4b010f8a-5e8b-45c1-a3c8-7b8821c9246e.png)


### Steps to reproduce the bug

binary is attached
[dd.zip](https://github.com/angr/angr/files/10327857/dd.zip)


### Environment

_No response_

### Additional context

_No response_


Discussion:
Looks like a bug in EagerReturnSimplifier.
You can disable EagerReturnSimplifier and decompile again to see if the issue goes away.
After setting` (EagerReturnsSimplifier, False),` in `optimization_pass\__init__.py`:
I have the following code:
```
int argmatch_to_argument(void *a0, unsigned long long *a1, void *a2, unsigned int a3)
{
    unsigned long long v1;  // r13
    unsigned long long v2;  // rax
    unsigned long long *v3;  // rbx
    unsigned long long v4;  // rax
    void *v5;  // r15
    unsigned long long v6;  // rax

    v1 = *(a1);
    if (*(a1) != 0)
    {
        v2 = bcmp(a0, a2, a3);
        if (v2 != 0)
        {
            v5 = a2 + a3;
            v3 = &a1[1];
            while (true)
            {
                v1 = *(v3);
                if (*(v3) == 0)
                {
                    break;
                }
                break;
                v4 = bcmp(a0, v5, a3);
                v5 += a3;
                v3 = &v3[1];
            }
            if (true)
            {
                v1 = 0;
            }
            else if (v4 == 0 && *(v3) != 0)
            {
                v6 = v1;
                return v6;
            }
        }
    }
    if (*(a1) == 0 || v2 != 0)
    {
        v1 = 0;
    }
    v6 = v1;
    return v6;
}
```
though `v4 ` is used in condition "`else if (v4 == 0 && *(v3) != 0)`", it is out of `while(true)` loop.

Resolving patch:
diff --git a/angr/analyses/decompiler/region_simplifiers/expr_folding.py b/angr/analyses/decompiler/region_simplifiers/expr_folding.py
--- a/angr/analyses/decompiler/region_simplifiers/expr_folding.py
+++ b/angr/analyses/decompiler/region_simplifiers/expr_folding.py
@@ -400,6 +400,8 @@ def _handle_Loop(self, node: LoopNode, **kwargs):
 class StoreStatementFinder(SequenceWalker):
     """
     Determine if there are any Store statements between two given statements.
+
+    This class overrides _handle_Sequence() and _handle_MultiNode() to ensure they traverse nodes from top to bottom.
     """
     def __init__(self, node, intervals: Iterable[Tuple[StatementLocation,LocationBase]]):
         handlers = {
@@ -423,6 +425,20 @@ def __init__(self, node, intervals: Iterable[Tuple[StatementLocation,LocationBas
         super().__init__(handlers)
         self.walk(node)
 
+    def _handle_Sequence(self, node, **kwargs):
+        i = 0
+        while i < len(node.nodes):
+            node_ = node.nodes[i]
+            self._handle(node_, parent=node, index=i)
+            i += 1
+
+    def _handle_MultiNode(self, node, **kwargs):
+        i = 0
+        while i < len(node.nodes):
+            node_ = node.nodes[i]
+            self._handle(node_, parent=node, index=i)
+            i += 1
+
     def _handle_Block(self, node: ailment.Block, **kwargs):
         stmt_loc = StatementLocation(node.addr, node.idx, None)
         for idx, stmt in enumerate(node.statements):
diff --git a/angr/analyses/decompiler/sequence_walker.py b/angr/analyses/decompiler/sequence_walker.py
--- a/angr/analyses/decompiler/sequence_walker.py
+++ b/angr/analyses/decompiler/sequence_walker.py
@@ -54,10 +54,14 @@ def _handle_Code(self, node: CodeNode, **kwargs):
         return CodeNode(new_inner_node, node.reaching_condition)
 
     def _handle_Sequence(self, node, **kwargs):
-        i = 0
         nodes_copy = list(node.nodes)
         changed = False
-        while i < len(nodes_copy):
+
+        # we iterate backwards because users of this function may invoke insert_node() directly to insert nodes to the
+        # parent node, either before the current node or after the current node. iterating backwards allows us to
+        # ensure `i` always points to the right index in node.nodes, even after custom insertions.
+        i = len(nodes_copy) - 1
+        while i > -1:
             node_ = nodes_copy[i]
             new_node = self._handle(node_, parent=node, index=i)
             if new_node is not None:
@@ -66,7 +70,7 @@ def _handle_Sequence(self, node, **kwargs):
                     node.nodes[i] = new_node
                 else:
                     nodes_copy[i] = new_node
-            i += 1
+            i -= 1
 
         if not changed:
             return None
@@ -75,16 +79,17 @@ def _handle_Sequence(self, node, **kwargs):
         return SequenceNode(node.addr, nodes=nodes_copy)
 
     def _handle_MultiNode(self, node, **kwargs):
-        i = 0
         changed = False
         nodes_copy = list(node.nodes)
-        while i < len(nodes_copy):
+
+        i = len(nodes_copy) - 1
+        while i > -1:
             node_ = nodes_copy[i]
             new_node = self._handle(node_, parent=node, index=i)
             if new_node is not None:
                 changed = True
                 node.nodes[i] = new_node
-            i += 1
+            i -= 1
         return None if not changed else node
 
     def _handle_SwitchCase(self, node, **kwargs):


Test patch: