Repository: angr/angr

Issue:
SimMemoryError occurs when using CFGFast

Hi!

I have been trying to analyze this binary [cgibin.zip](https://github.com/angr/angr/files/6573162/cgibin.zip), which comes from a mipsel firmware. And when I try to generate the CFG graph by using CFGFast, the SimMemoryError occurs. Especially, the `servicecgi_main` function is my target and the function starts at 0x40cdf8.
Here is my code:
`cfg = self.project.analyses.CFGFast(function_starts=[0x40cdf8])`
And here is the error trace back:
```
Traceback (most recent call last):
  File "/home/jackfromeast/bamboofuzz/fuzz-scripts/angr_taint_engine/taint_tracking.py", line 652, in setup
    cfg = self.project.analyses.CFGFast(function_starts=[0x40cdf8])
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/analysis.py", line 115, in __call__
    oself.__init__(*args, **kwargs)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 663, in __init__
    self._analyze()
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/forward_analysis/forward_analysis.py", line 216, in _analyze
    self._analysis_core_baremetal()
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/forward_analysis/forward_analysis.py", line 368, in _analysis_core_baremetal
    self._process_job_and_get_successors(job_info)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/forward_analysis/forward_analysis.py", line 386, in _process_job_and_get_successors
    successors = self._get_successors(job)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 1099, in _get_successors
    jobs = self._scan_block(job)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 1417, in _scan_block
    entries = self._scan_irsb(cfg_job, current_func_addr)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 1637, in _scan_irsb
    entries += self._create_jobs(target, jumpkind, function_addr, irsb, addr, cfg_node, ins_addr,
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_fast.py", line 1711, in _create_jobs
    resolved, resolved_targets, ij = self._indirect_jump_encountered(addr, cfg_node, irsb,
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_base.py", line 2400, in _indirect_jump_encountered
    resolved, resolved_targets = self._resolve_indirect_jump_timelessly(addr, irsb, func_addr, jumpkind)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/cfg_base.py", line 2340, in _resolve_indirect_jump_timelessly
    r, resolved_targets = res.resolve(self, addr, func_addr, block, jumpkind)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py", line 65, in resolve
    prop = self.project.analyses.Propagator(block=block)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/analysis.py", line 115, in __call__
    oself.__init__(*args, **kwargs)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/propagator.py", line 530, in __init__
    self._analyze()
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/propagator.py", line 678, in _analyze
    self._analysis_core_graph()
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/forward_analysis/forward_analysis.py", line 240, in _analysis_core_graph
    changed, output_state = self._run_on_node(n, job_state)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/propagator.py", line 578, in _run_on_node
    state = engine.process(state, block=block, project=self.project, base_state=self._base_state,
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/engine_base.py", line 28, in process
    self._process(state, None, block=kwargs.pop('block', None))
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/engine_vex.py", line 28, in _process
    super()._process(state, successors, block=block, whitelist=whitelist, **kwargs)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/engines/light/engine.py", line 142, in _process
    self._process_Stmt(whitelist=whitelist)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/engines/light/engine.py", line 163, in _process_Stmt
    self._handle_Stmt(stmt)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/engines/light/engine.py", line 191, in _handle_Stmt
    getattr(self, handler)(stmt)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/engine_vex.py", line 122, in _handle_Put
    self.state.store_register(stmt.offset, size, data)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/analyses/propagator/propagator.py", line 184, in store_register
    self._registers.store(offset, value, size=size)
  File "/home/jackfromeast/miniconda3/envs/bamboofuzz/lib/python3.8/site-packages/angr/storage/memory_mixins/size_resolution_mixin.py", line 42, in store
    raise SimMemoryError("Not enough data for store")
angr.errors.SimMemoryError: Not enough data for store
```
Besides, when I try to use CFGEmulated instead, the following error occurred.
`cfg = self.project.analyses.CFGEmulated(starts=[0x40cdf8], call_depth=2)`
```
WARNING | 2021-06-01 11:41:43,455 | cle.backends.externs | Symbol was allocated without a known size; emulation may fail if it is used non-opaquely: __ctype_b
WARNING | 2021-06-01 11:41:43,459 | cle.loader | For more information about "Symbol was allocated without a known size", see https://docs.angr.io/extending-angr/environment#simdata
WARNING | 2021-06-01 11:41:44,082 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,111 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,138 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,161 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,183 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,204 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,236 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,257 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,270 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,302 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,326 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,340 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,368 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,390 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,426 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,438 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,469 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,497 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,518 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,536 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,561 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,576 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,603 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,621 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,641 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,663 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,707 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,716 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,727 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,750 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,762 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:44,803 | angr.analyses.cfg.indirect_jump_resolvers.mips_elf_fast | Failed to determine value of register gp for function 0x40cdf8.
WARNING | 2021-06-01 11:41:45,924 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function cgibin_parse_request (0x403694)>.
WARNING | 2021-06-01 11:41:45,926 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function cgibin_parse_request (0x403694)>.
WARNING | 2021-06-01 11:41:45,927 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function sess_ispoweruser (0x4097f8)>.
WARNING | 2021-06-01 11:41:45,928 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function sub_40cd50 (0x40cd50)>.
WARNING | 2021-06-01 11:41:45,929 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function sub_40cd50 (0x40cd50)>.
WARNING | 2021-06-01 11:41:45,931 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function sub_40cd50 (0x40cd50)>.
WARNING | 2021-06-01 11:41:45,932 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function lxmldbc_system (0x413394)>.
WARNING | 2021-06-01 11:41:45,932 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function lxmldbc_system (0x413394)>.
WARNING | 2021-06-01 11:41:45,933 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function lxmldbc_system (0x413394)>.
WARNING | 2021-06-01 11:41:45,936 | angr.analyses.variable_recovery.variable_recovery_fast | Unknown calling convention for <Function cgibin_clean_tempfiles (0x404f90)>.
```
Any idea about why this happened and how to fix this problem?


Discussion:
This looks like a bug in Propagator. I'll fix it.

Resolving patch:
diff --git a/angr/analyses/propagator/top_checker_mixin.py b/angr/analyses/propagator/top_checker_mixin.py
--- a/angr/analyses/propagator/top_checker_mixin.py
+++ b/angr/analyses/propagator/top_checker_mixin.py
@@ -5,8 +5,9 @@
 
 class TopCheckerMixin(SimEngineLightMixin):
     def _is_top(self, expr) -> bool:
-        if isinstance(expr, claripy.ast.Base) and expr.op == "BVS" and expr.args[0] == 'TOP':
-            return True
+        if isinstance(expr, claripy.ast.Base):
+            if "TOP" in expr.variables:
+                return True
         return False
 
     def _top(self, size: int):
diff --git a/angr/analyses/variable_recovery/engine_vex.py b/angr/analyses/variable_recovery/engine_vex.py
--- a/angr/analyses/variable_recovery/engine_vex.py
+++ b/angr/analyses/variable_recovery/engine_vex.py
@@ -232,6 +232,44 @@ def _handle_Mul(self, expr):
         r = self.state.top(result_size)
         return RichR(r)
 
+    def _handle_DivMod(self, expr):
+        arg0, arg1 = expr.args
+        r0 = self._expr(arg0)
+        r1 = self._expr(arg1)
+
+        result_size = expr.result_size(self.tyenv)
+        if r0.data.concrete and r1.data.concrete:
+            # constants
+            try:
+                signed = "U" in expr.op  # Iop_DivModU64to32 vs Iop_DivMod
+                from_size = r0.data.size()
+                to_size = r1.data.size()
+                if signed:
+                    quotient = (r0.data.SDiv(claripy.SignExt(from_size - to_size, r1.data)))
+                    remainder = (r0.data.SMod(claripy.SignExt(from_size - to_size, r1.data)))
+                    quotient_size = to_size
+                    remainder_size = to_size
+                    result = claripy.Concat(
+                        claripy.Extract(remainder_size - 1, 0, remainder),
+                        claripy.Extract(quotient_size - 1, 0, quotient)
+                    )
+                else:
+                    quotient = (r0.data // claripy.ZeroExt(from_size - to_size, r1.data))
+                    remainder = (r0.data % claripy.ZeroExt(from_size - to_size, r1.data))
+                    quotient_size = to_size
+                    remainder_size = to_size
+                    result = claripy.Concat(
+                        claripy.Extract(remainder_size - 1, 0, remainder),
+                        claripy.Extract(quotient_size - 1, 0, quotient)
+                    )
+
+                return RichR(result)
+            except ZeroDivisionError:
+                pass
+
+        r = self.state.top(result_size)
+        return RichR(r)
+
     def _handle_Div(self, expr):
         arg0, arg1 = expr.args
         r0 = self._expr(arg0)
diff --git a/angr/engines/light/engine.py b/angr/engines/light/engine.py
--- a/angr/engines/light/engine.py
+++ b/angr/engines/light/engine.py
@@ -299,6 +299,8 @@ def _handle_Binop(self, expr):
             handler = '_handle_Sub'
         elif expr.op.startswith('Iop_Mul'):
             handler = "_handle_Mul"
+        elif expr.op.startswith('Iop_DivMod'):
+            handler = "_handle_DivMod"
         elif expr.op.startswith('Iop_Div'):
             handler = "_handle_Div"
         elif expr.op.startswith('Iop_Xor'):
@@ -470,6 +472,36 @@ def _handle_Mul(self, expr):
 
         return expr_0 * expr_1
 
+    def _handle_DivMod(self, expr):
+        args, r = self._binop_get_args(expr)
+        if args is None: return r
+        expr_0, expr_1 = args
+
+        if self._is_top(expr_0) or self._is_top(expr_1):
+            return self._top(expr.result_size(self.tyenv))
+
+        signed = "U" in expr.op  # Iop_DivModU64to32 vs Iop_DivMod
+        from_size = expr_0.size()
+        to_size = expr_1.size()
+        if signed:
+            quotient = (expr_0.SDiv(claripy.SignExt(from_size - to_size, expr_1)))
+            remainder = (expr_1.SMod(claripy.SignExt(from_size - to_size, expr_1)))
+            quotient_size = to_size
+            remainder_size = to_size
+            return claripy.Concat(
+                claripy.Extract(remainder_size - 1, 0, remainder),
+                claripy.Extract(quotient_size - 1, 0, quotient)
+            )
+        else:
+            quotient = (expr_0 // claripy.ZeroExt(from_size - to_size, expr_1))
+            remainder = (expr_0 % claripy.ZeroExt(from_size - to_size, expr_1))
+            quotient_size = to_size
+            remainder_size = to_size
+            return claripy.Concat(
+                claripy.Extract(remainder_size - 1, 0, remainder),
+                claripy.Extract(quotient_size - 1, 0, quotient)
+            )
+
     def _handle_Div(self, expr):
         args, r = self._binop_get_args(expr)
         if args is None: return r
@@ -637,7 +669,12 @@ def _handle_MBE(self, expr):  # pylint:disable=unused-argument
 
     def _handle_32HLto64(self, expr):
         args, r = self._binop_get_args(expr)
-        if args is None: return r
+        if args is None:
+            if r is not None:
+                # the size of r should be 32 but we need to return a 64-bit expression
+                assert r.size() == 32
+                r = claripy.ZeroExt(32, r)
+            return r
 
         return None
 


Test patch: