Repository: angr/angr

Issue:
Decompilation error in ARM binary's thunk
### Description

Context:

I wrote a program containing 100 calls to `gt32` for unit test:
```c
int32_t gt32(int32_t a, int32_t b){
	return a > b;
}

int main(){
	sink(gt32(3236716579, 3221347276));
	sink(gt32(1379110817, 1113518874));
	sink(gt32(896556042, 2704120162));
        ......
	sink(gt32(3002246636, 3351598752));
	sink(gt32(3890490740, 2816674071));
	sink(gt32(3626833283, 1567037049));
}
```
I compiled it using arm-linux-gnueabihf-gcc. Then I found gcc inserted a [thunk](https://stackoverflow.com/questions/2641489/what-is-a-thunk) into the binary, and optimized the last call to `gt32` into a call to the thunk  `sub_401059`.
![image](https://user-images.githubusercontent.com/35004522/216891981-04d0e6d9-4933-471c-9256-37bb28ff28a1.png)

Following are some disassembly/decompilation results:

Disassembly result of the thunk (correct):
![image](https://user-images.githubusercontent.com/35004522/216888435-6c83e95c-9a5f-4079-8ad7-b7b9ed9bbede.png)
AIL representation (wrong):
![image](https://user-images.githubusercontent.com/35004522/216888480-bddd7315-e145-47b7-8854-28b229fd3513.png)
angr-mangement's pseudo code (wrong):
![image](https://user-images.githubusercontent.com/35004522/216888541-eff31d5a-f2f1-479e-af2b-9eb4c6a36690.png)
IDA Pro's pseudo code (correct):
![image](https://user-images.githubusercontent.com/35004522/216888612-02f51563-71cb-4b2a-8b04-86c5173e6a1b.png)

### Steps to reproduce the bug

[gt32_arm.zip](https://github.com/angr/angr/files/10660065/gt32_arm.zip)
```
$ angr-management gt32_arm
```

### Environment

```
/home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/angr/misc/bug_report.py:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
angr environment report
=============================
Date: 2023-02-06 13:49:35.278752
Running in virtual environment at /home/bluesadi/virtualenvs/Yasat
Platform: linux-x86_64
Python version: 3.8.10 (default, Jun 22 2022, 20:18:18) 
[GCC 9.4.0]
######## angr #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/angr
Pip version angr 9.2.36
Couldn't find git info
######## ailment #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/ailment
Pip version ailment 9.2.36
Couldn't find git info
######## cle #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/cle
Pip version cle 9.2.36
Couldn't find git info
######## pyvex #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/pyvex
Pip version pyvex 9.2.36
Couldn't find git info
######## claripy #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/claripy
Pip version claripy 9.2.36
Couldn't find git info
######## archinfo #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/archinfo
Pip version archinfo 9.2.36
Couldn't find git info
######## z3 #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/z3
Pip version z3-solver 4.10.2.0
Couldn't find git info
######## unicorn #########
Python found it in /home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/unicorn
Pip version unicorn 2.0.1.post1
Couldn't find git info
######### Native Module Info ##########
angr: <CDLL '/home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/angr/state_plugins/../lib/angr_native.so', handle 2746970 at 0x7efe99c5cb50>
unicorn: <CDLL '/home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/unicorn/lib/libunicorn.so.2', handle 201ac60 at 0x7efe9ee77a60>
pyvex: <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7efe9f9fa5b0>
z3: <CDLL '/home/bluesadi/virtualenvs/Yasat/lib/python3.8/site-packages/z3/lib/libz3.so', handle 246da60 at 0x7efe9bbbd0a0>
```

### Additional context

It's possibly an extreme case because the thunk optimization here seems meaningless (I don't know why gcc does that), and this optimization only exists in a small part of my other similar test binaries.


Discussion:
Btw, the "Lifter IR" button doesn't work on both Ubuntu and Windows:
![image](https://user-images.githubusercontent.com/35004522/216913382-ae0701c8-b19d-42e4-b160-91a2637d531e.png)
with error message:
```
WARNING | 20:18:37 | angrmanagement.data.jobs.flirt_signature_recognition | No FLIRT signatures exist for architecture ARMHF.
WARNING | 20:18:38 | angr.analyses.reaching_definitions.function_handler | Please implement the indirect function handler with your own logic.
ERROR | 20:18:40 | angr.analyses.disassembly | Operand parsing failed for instruction 0x400505:	ite	gt. 1 operands are parsed, while 0 are expected.
ERROR | 20:18:42 | angr.analyses.disassembly | Operand parsing failed for instruction 0x400505:	ite	gt. 1 operands are parsed, while 0 are expected.
ERROR | 20:18:51 | angr.analyses.disassembly | Operand parsing failed for instruction 0x400505:	ite	gt. 1 operands are parsed, while 0 are expected.
WARNING | 20:19:01 | angr.analyses.decompiler.optimization_passes.engine_base | Unsupported expression type ITE.
WARNING | 20:19:01 | angr.analyses.decompiler.optimization_passes.engine_base | Unsupported expression type ITE.
ERROR | 20:19:06 | angr.analyses.disassembly | Operand parsing failed for instruction 0x400505:	ite	gt. 1 operands are parsed, while 0 are expected.
```
Hi @bluesadi, for the **operand parsing** part, I just tested the binary with angr (version 9.2.38). The flowing error message in your case didn't show up.
```
ERROR | 20:18:40 | angr.analyses.disassembly | Operand parsing failed for instruction 0x400505:	ite	gt. 1 operands are parsed, while 0 are expected.
```
The instruction at `0x400505` can be correctly parsed.
![image](https://user-images.githubusercontent.com/31790712/218020843-50dc6a7d-a7b8-42f0-bb95-fc6a5a0df26e.png)

I have fixed arm32 disassembly in 9.2.35 in commit 56ea5fa16ac653f69902222d95eccd1bc6384552. Could you check again about the version of angr and angr-management? Let me know if you still find operand parsing fails.

Thanks for your reminding @xxr0ss. I forgot to upgrade angr-management on my Windows. 

I just upgraded angr-mangement and angr to the latest 9.2.37 version. Now it doesn't yield any errors, but the "Lifter IR" button still doesn't work (only happends to ARM binaries).

[binaries.zip](https://github.com/angr/angr/files/10704566/binaries.zip)

![image](https://user-images.githubusercontent.com/35004522/218026042-00d7589a-8983-4ffa-a6ac-f371997b75fd.png)


Resolving patch:
diff --git a/angr/analyses/decompiler/clinic.py b/angr/analyses/decompiler/clinic.py
--- a/angr/analyses/decompiler/clinic.py
+++ b/angr/analyses/decompiler/clinic.py
@@ -93,6 +93,8 @@ def __init__(
         self.reaching_definitions: Optional[ReachingDefinitionsAnalysis] = None
         self._cache = cache
 
+        self._new_block_addrs = set()
+
         # sanity checks
         if not self.kb.functions:
             l.warning("No function is available in kb.functions. It will lead to a suboptimal conversion result.")
@@ -583,7 +585,7 @@ def _replace_tail_jumps_with_calls(self, ail_graph: networkx.DiGraph) -> network
                             block.statements[-1] = call_stmt
 
                             ret_stmt = ailment.Stmt.Return(None, None, [], **last_stmt.tags)
-                            ret_block = ailment.Block(last_stmt.ins_addr, 1, statements=[ret_stmt])
+                            ret_block = ailment.Block(self.new_block_addr(), 1, statements=[ret_stmt])
                             ail_graph.add_edge(block, ret_block)
                         else:
                             stmt = ailment.Stmt.Call(None, target, **last_stmt.tags)
@@ -1357,6 +1359,19 @@ def parse_variable_addr(self, addr: ailment.Expr.Expression) -> Optional[Tuple[A
                 return op0, op1  # best-effort guess
         return None, None
 
+    def new_block_addr(self) -> int:
+        """
+        Return a block address that does not conflict with any existing blocks.
+
+        :return:    The block address.
+        """
+        if self._new_block_addrs:
+            new_addr = max(self._new_block_addrs) + 1
+        else:
+            new_addr = max(self.function.block_addrs_set) + 2048
+        self._new_block_addrs.add(new_addr)
+        return new_addr
+
     @staticmethod
     @timethis
     def remove_empty_nodes(graph: networkx.DiGraph) -> networkx.DiGraph:


Test patch: