Repository: angr/angr

Issue:
AssertionError when using CFGFast(normalize=True) on ARM binary
### Description

Hello, I'm trying to analyze basic blocks in an ARM binary using CFGFast with normalize=True. I get the following AssertionError with my binary as input:

```
Traceback (most recent call last):
  File "script.py", line 6, in <module>
    cfg = proj.analyses.CFGFast(normalize=True)
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/analysis.py", line 144, in __call__
    r = w(*args, **kwargs)
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/analysis.py", line 129, in wrapper
    oself.__init__(*args, **kwargs)
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 851, in __init__
    self._analyze()
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/forward_analysis/forward_analysis.py", line 255, in _analyze
    self._post_analysis()
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 1543, in _post_analysis
    self.make_functions()
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/cfg/cfg_base.py", line 1455, in make_functions
    removed_functions_b, adjusted_cfgnodes = self._process_irrational_function_starts(
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/cfg/cfg_base.py", line 1867, in _process_irrational_function_starts
    self._merge_cfgnodes(cfgnode_0, cfgnode_1)
  File "/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/analyses/cfg/cfg_base.py", line 381, in _merge_cfgnodes
    assert cfgnode_0.addr + cfgnode_0.size == cfgnode_1.addr
AssertionError
```

### Steps to reproduce the bug

Attaching script and binary to reproduce the AssertionError: 
[normalize_bug.zip](https://github.com/angr/angr/files/10472642/normalize_bug.zip)

To reproduce the error with angr-9.2.34: `python script.py bin.out`



### Environment

angr environment report
=============================
Date: 2023-01-21 14:59:44.998071
!!! running in global environment.  Are you sure? !!!
Platform: linux-x86_64
Python version: 3.8.16 (default, Jan 17 2023, 23:13:24) 
[GCC 11.2.0]
######## angr #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr
Pip version angr 9.2.34
Couldn't find git info
######## ailment #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/ailment
Pip version ailment 9.2.34
Couldn't find git info
######## cle #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/cle
Pip version cle 9.2.34
Couldn't find git info
######## pyvex #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/pyvex
Pip version pyvex 9.2.34
Couldn't find git info
######## claripy #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/claripy
Pip version claripy 9.2.34
Couldn't find git info
######## archinfo #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/archinfo
Pip version archinfo 9.2.34
Couldn't find git info
######## z3 #########
Python found it in /home/yashas/miniconda3/envs/angr-9.2.34/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/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/unicorn
Pip version unicorn 2.0.1.post1
Couldn't find git info
######### Native Module Info ##########
angr: <CDLL '/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/angr/state_plugins/../lib/angr_native.so', handle 2906430 at 0x7f634d4be880>
unicorn: <CDLL '/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/unicorn/lib/libunicorn.so.2', handle 227c660 at 0x7f6353d8a730>
pyvex: <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7f6353247eb0>
z3: <CDLL '/home/yashas/miniconda3/envs/angr-9.2.34/lib/python3.8/site-packages/z3/lib/libz3.so', handle 2635500 at 0x7f634f4a8c40>

### Additional context

_No response_


Discussion:
Thank you for reporting. Can we use your binary as a test case?
Yes, the binary provided can be used as a test
Awesome. Thanks a lot!

Resolving patch:
diff --git a/angr/analyses/cfg/cfg_base.py b/angr/analyses/cfg/cfg_base.py
--- a/angr/analyses/cfg/cfg_base.py
+++ b/angr/analyses/cfg/cfg_base.py
@@ -1861,6 +1861,8 @@ def _process_irrational_function_starts(self, functions, predetermined_function_
                 if (
                     len(func_0.block_addrs_set) == 1
                     and len(out_edges) == 1
+                    and out_edges[0][0].addr == cfgnode_0.addr
+                    and out_edges[0][0].size == cfgnode_0.size
                     and self.project.factory.block(cfgnode_0.addr, strict_block_end=True).size > cfgnode_0.size
                 ):
                     cfgnode_1_merged = True
diff --git a/angr/analyses/cfg/cfg_fast.py b/angr/analyses/cfg/cfg_fast.py
--- a/angr/analyses/cfg/cfg_fast.py
+++ b/angr/analyses/cfg/cfg_fast.py
@@ -436,6 +436,7 @@ class CFGJob:
     JOB_TYPE_FUNCTION_PROLOGUE = "Function-prologue"
     JOB_TYPE_COMPLETE_SCANNING = "Complete-scanning"
     JOB_TYPE_IFUNC_HINTS = "ifunc-hints"
+    JOB_TYPE_DATAREF_HINTS = "dataref-hints"
 
     def __init__(
         self,
@@ -2529,6 +2530,18 @@ def _process_irsb_data_ref_inlined_data(self, irsb, ref):
                     data_size=None,
                     data_type=MemoryDataSort.Unknown,
                 )
+
+                if sec_2nd.is_executable and not self._seg_list.is_occupied(v):
+                    # create a new CFG job
+                    ce = CFGJob(
+                        v,
+                        v,
+                        "Ijk_Boring",
+                        job_type=CFGJob.JOB_TYPE_DATAREF_HINTS,
+                    )
+                    self._pending_jobs.add_job(ce)
+                    self._register_analysis_job(v, ce)
+
                 return
 
             # attempt 2: pc + offset
diff --git a/angr/analyses/cfg/indirect_jump_resolvers/jumptable.py b/angr/analyses/cfg/indirect_jump_resolvers/jumptable.py
--- a/angr/analyses/cfg/indirect_jump_resolvers/jumptable.py
+++ b/angr/analyses/cfg/indirect_jump_resolvers/jumptable.py
@@ -1763,7 +1763,10 @@ def _try_resolve_targets_ite(
         state.add_constraints(cond == 1)
         # load the target
         target_expr = temps[ite_stmt.data.iftrue.tmp]
-        jump_table = state.solver.eval_upto(target_expr, self._max_targets + 1)
+        try:
+            jump_table = state.solver.eval_upto(target_expr, self._max_targets + 1)
+        except SimError:
+            return None
         entry_size = len(target_expr) // self.project.arch.byte_width
 
         if len(jump_table) == self._max_targets + 1:


Test patch:
diff --git a/tests/test_cfgfast.py b/tests/test_cfgfast.py
--- a/tests/test_cfgfast.py
+++ b/tests/test_cfgfast.py
@@ -1068,6 +1068,17 @@ def test_data_references_wide_string(self):
         for testme in ("SOSNEAKY", "Welcome to the admin console, trusted user!\n", "Go away!\n", "Username: \n"):
             assert testme.encode("utf-16-le") in recovered_strings
 
+    def test_arm_function_hints_from_data_references(self):
+        path = os.path.join(test_location, "armel", "sha224sum")
+        proj = angr.Project(path, auto_load_libs=False)
+
+        proj.analyses.CFGFast(data_references=True)
+        funcs = proj.kb.functions
+        assert funcs.contains_addr(0x129C4)
+        func = funcs[0x129C4]
+        assert len(list(func.blocks)) == 1
+        assert list(func.blocks)[0].size == 16
+
 
 if __name__ == "__main__":
     unittest.main()