Repository: angr/angr

Issue:
VariableRecovery ClaripyOperationError Exception
**Describe the bug**
When performing variable recovery, i am receiving a `ClaripyOperationError` exception.

**Environment Information**
Mechaphish docker
```
python -m angr.misc.bug_report
angr environment report
=============================
Date: 2019-02-03 17:54:47.574843
Running in virtual environment at /home/angr/.virtualenvs/angr
Platform: linux-x86_64
Python version: 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0]
######## angr #########
Python found it in /home/angr/angr-dev/angr/angr
Pip version angr 8.18.10.25
Git info:
        Current commit 86e2aa94a88cf0daf4ee3bb946f82f35c5e329e2 from branch MemoryWatcher
        Checked out from remote bannsec: https://github.com/bannsec/angr.git
######## ailment #########
Python found it in /home/angr/angr-dev/ailment/ailment
Pip version ailment 8.18.10.25
Git info:
        Current commit 284a67b1b5db5c950bebf4b6a096a127318474e8 from branch master
        Checked out from remote origin: https://github.com/angr/ailment
######## cle #########
Python found it in /home/angr/angr-dev/cle/cle
Pip version cle 8.18.10.25
Git info:
        Current commit 28cbf5c5888b374b173410ef2e508b4eab7c02aa from branch master
        Checked out from remote origin: https://github.com/angr/cle
######## pyvex #########
Python found it in /home/angr/angr-dev/pyvex/pyvex
Pip version pyvex 8.18.10.25
Git info:
        Current commit 36ba7fefbece6c11a27933b5151b450a87856302 from branch master
        Checked out from remote origin: https://github.com/angr/pyvex
######## claripy #########
Python found it in /home/angr/angr-dev/claripy/claripy
Pip version claripy 8.18.10.25
Git info:
        Current commit 16457ff5b60f348b9246d4e2df4ff62052f89cc6 from branch master
        Checked out from remote origin: https://github.com/angr/claripy
######## archinfo #########
Python found it in /home/angr/angr-dev/archinfo/archinfo
Pip version archinfo 8.18.10.25
Git info:
        Current commit bbac424873989d9cc4ce3ed01b7003ace04b4756 from branch master
        Checked out from remote origin: https://github.com/angr/archinfo
######## ana #########
Python found it in /home/angr/.virtualenvs/angr/lib/python3.6/site-packages/ana
Pip version ana 0.5
Couldn't find git info
######## z3 #########
Python found it in /home/angr/.virtualenvs/angr/lib/python3.6/site-packages/z3
Pip version not found!
Couldn't find git info
######## unicorn #########
Python found it in /home/angr/.virtualenvs/angr/lib/python3.6/site-packages/unicorn
Pip version unicorn 1.0.2
Couldn't find git info
######### Native Module Info ##########
angr: <CDLL '/home/angr/angr-dev/angr/angr/lib/angr_native.so', handle 2714120 at 0x7f2cb50d2438>
unicorn: <CDLL '/home/angr/.virtualenvs/angr/lib/python3.6/site-packages/unicorn/lib/libunicorn.so', handle 2284200 at 0x7f2cb8f4be80>
pyvex: <cffi.api._make_ffi_library.<locals>.FFILibrary object at 0x7f2cb9b9bf28>
z3: <CDLL '/home/angr/.virtualenvs/angr/lib/python3.6/site-packages/z3/lib/libz3.so', handle 2d44ae0 at 0x7f2cb3a8ceb8>
```

**To Reproduce**
```python
import angr
proj = angr.Project('./forgetful_commander', auto_load_libs=False)
cfg = proj.analyses.CFG()
x = cfg.functions[0x4022c8]
proj.analyses.VariableRecovery(x)
```

**Additional context**
```
In [8]: proj.analyses.VariableRecovery(x)
WARNING | 2019-02-03 17:53:52,086 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_33]|Reg 68, 4B>, <0x4022c8[ir_35]|Reg 68, 4B>}.
WARNING | 2019-02-03 17:53:52,088 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_36]|Reg 40, 4B>, <0x4022c8[ir_28]|Reg 40, 4B>}.
WARNING | 2019-02-03 17:53:52,089 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_37]|Reg 44, 4B>, <0x4022c8[ir_29]|Reg 44, 4B>}.
WARNING | 2019-02-03 17:53:52,089 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_30]|Reg 48, 4B>, <0x4022c8[ir_38]|Reg 48, 4B>}.
WARNING | 2019-02-03 17:53:52,090 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_39]|Reg 52, 4B>, <0x4022c8[ir_31]|Reg 52, 4B>}.
WARNING | 2019-02-03 17:53:52,094 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_15]|Reg 32, 4B>, <0x4022c8[ir_40]|Reg 32, 4B>}.
WARNING | 2019-02-03 17:53:52,098 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_41]|Reg 8, 4B>, <0x4022c8[ir_32]|Reg 8, 4B>}.
WARNING | 2019-02-03 17:53:52,102 | angr.keyed_region | Overlapping objects {<0x4022c8[ir_42]|Reg 152, 4B>, <0x4022c8[ir_34]|Reg 152, 4B>}.
---------------------------------------------------------------------------
ClaripyOperationError                     Traceback (most recent call last)
<ipython-input-8-de8d6084dd49> in <module>
----> 1 proj.analyses.VariableRecovery(x)

~/angr-dev/angr/angr/analyses/analysis.py in __call__(self, *args, **kwargs)
    107
    108         oself._show_progressbar = show_progressbar
--> 109         oself.__init__(*args, **kwargs)
    110         return oself
    111

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in __init__(self, func, max_iterations)
    402         self._node_iterations = defaultdict(int)
    403
--> 404         self._analyze()
    405
    406     #

~/angr-dev/angr/angr/analyses/forward_analysis.py in _analyze(self)
    555             # We have a base graph to follow. Just handle the current job.
    556
--> 557             self._analysis_core_graph()
    558
    559         self._post_analysis()

~/angr-dev/angr/angr/analyses/forward_analysis.py in _analysis_core_graph(self)
    578                 break
    579
--> 580             changed, output_state = self._run_on_node(n, job_state)
    581
    582             # output state of node n is input state for successors to node n

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in _run_on_node(self, node, state)
    467                                                      addr=node.addr,
    468                                                      size=node.size,
--> 469                                                      opt_level=0  # disable the optimization in order to have
    470                                                                   # instruction-level analysis results
    471                                                      )

~/angr-dev/angr/angr/factory.py in successors(self, *args, **kwargs)
     47         """
     48
---> 49         return self.project.engines.successors(*args, **kwargs)
     50
     51     def blank_state(self, **kwargs):

~/angr-dev/angr/angr/engines/hub.py in successors(self, state, addr, jumpkind, default_engine, procedure_engine, engines, **kwargs)
    126         for engine in engines:
    127             if engine.check(state, **kwargs):
--> 128                 r = engine.process(state, **kwargs)
    129                 if r.processed:
    130                     return r

~/angr-dev/angr/angr/engines/vex/engine.py in process(self, state, irsb, skip_stmts, last_stmt, whitelist, inline, force_addr, insn_bytes, size, num_inst, traceflags, thumb, extra_stop_points, opt_level, **kwargs)
    140                 thumb=thumb,
    141                 extra_stop_points=extra_stop_points,
--> 142                 opt_level=opt_level)
    143
    144     def _check(self, state, *args, **kwargs):

~/angr-dev/angr/angr/engines/engine.py in process(***failed resolving arguments***)
     53         successors = new_state._inspect_getattr('sim_successors', successors)
     54         try:
---> 55             self._process(new_state, successors, *args, **kwargs)
     56         except SimException:
     57             if o.EXCEPTION_HANDLING not in old_state.options:

~/angr-dev/angr/angr/engines/vex/engine.py in _process(self, state, successors, irsb, skip_stmts, last_stmt, whitelist, insn_bytes, size, num_inst, traceflags, thumb, extra_stop_points, opt_level)
    191
    192             try:
--> 193                 self._handle_irsb(state, successors, irsb, skip_stmts, last_stmt, whitelist)
    194             except SimReliftException as e:
    195                 state = e.state

~/angr-dev/angr/angr/engines/vex/engine.py in _handle_irsb(self, state, successors, irsb, skip_stmts, last_stmt, whitelist)
    270                 state.scratch.stmt_idx = stmt_idx
    271                 state._inspect('statement', BP_BEFORE, statement=stmt_idx)
--> 272                 cont = self._handle_statement(state, successors, stmt)
    273                 state._inspect('statement', BP_AFTER)
    274                 if not cont:

~/angr-dev/angr/angr/engines/vex/engine.py in _handle_statement(self, state, successors, stmt)
    378
    379         # process it!
--> 380         s_stmt = translate_stmt(stmt, state)
    381         if s_stmt is not None:
    382             state.history.extend_actions(s_stmt.actions)

~/angr-dev/angr/angr/engines/vex/statements/__init__.py in translate_stmt(stmt, state)
     27         stmt_class = globals()[stmt_name]
     28         s = stmt_class(stmt, state)
---> 29         s.process()
     30         return s
     31     else:

~/angr-dev/angr/angr/engines/vex/statements/base.py in process(self)
     39         """
     40         # this is where we would choose between different analysis modes
---> 41         self._execute()
     42
     43     def _execute(self):

~/angr-dev/angr/angr/engines/vex/statements/wrtmp.py in _execute(self)
      8     def _execute(self):
      9         # get data and track data reads
---> 10         data = self._translate_expr(self.stmt.data)
     11         self.state.scratch.store_tmp(self.stmt.tmp, data.expr, data.reg_deps(), data.tmp_deps(),
     12                                      action_holder=self.actions

~/angr-dev/angr/angr/engines/vex/statements/base.py in _translate_expr(self, expr)
     46     def _translate_expr(self, expr):
     47         """Translates an IRExpr into a SimIRExpr."""
---> 48         e = translate_expr(expr, self.state)
     49         self._record_expr(e)
     50         return e

~/angr-dev/angr/angr/engines/vex/expressions/__init__.py in translate_expr(expr, state)
     12     l.debug("Processing expression %s", expr_name)
     13     e = expr_class(expr, state)
---> 14     e.process()
     15     return e
     16

~/angr-dev/angr/angr/engines/vex/expressions/base.py in process(self)
     38
     39         # this should change when additional analyses are implemented
---> 40         self._execute()
     41
     42         self._post_process()

~/angr-dev/angr/angr/engines/vex/expressions/geti.py in _execute(self)
     18
     19         # get it!
---> 20         self.expr = self.state.registers.load(self.offset, size)
     21
     22         if self.type.startswith('Ity_F'):

~/angr-dev/angr/angr/storage/memory.py in load(self, addr, size, condition, fallback, add_constraints, action, endness, inspect, disable_actions, ret_on_segv)
    802
    803             elif self.category == 'reg':
--> 804                 self.state._inspect('reg_read', BP_AFTER, reg_read_expr=r)
    805                 r = self.state._inspect_getattr("reg_read_expr", r)
    806

~/angr-dev/angr/angr/sim_state.py in _inspect(self, *args, **kwargs)
    257     def _inspect(self, *args, **kwargs):
    258         if self.has_plugin('inspect'):
--> 259             self.inspect.action(*args, **kwargs)
    260
    261     def _inspect_getattr(self, attr, default_value):

~/angr-dev/angr/angr/state_plugins/inspect.py in action(self, event_type, when, **kwargs)
    246             if bp.check(self.state, when):
    247                 l.debug("... FIRE")
--> 248                 bp.fire(self.state)
    249
    250     def make_breakpoint(self, event_type, *args, **kwargs):

~/angr-dev/angr/angr/state_plugins/inspect.py in fire(self, state)
    198                            "make modifications, then exit this shell to resume your analysis.")
    199         else:
--> 200             self.action(state)
    201
    202     def __repr__(self):

~/angr-dev/angr/angr/analyses/variable_recovery/variable_recovery.py in _hook_register_read(self, state)
    144         reg_read_length = state.inspect.reg_read_length
    145
--> 146         if reg_read_offset == state.arch.sp_offset and reg_read_length == state.arch.bytes:
    147             # TODO: make sure the sp is not overwritten by something that we are not tracking
    148             return

~/angr-dev/claripy/claripy/ast/base.py in __bool__(self)
    643         constraint solve. This caused tons of issues.
    644         """
--> 645         raise ClaripyOperationError('testing Expressions for truthiness does not do what you want, as these expressions can be symbolic')
    646
    647     def structurally_match(self, o):

ClaripyOperationError: testing Expressions for truthiness does not do what you want, as these expressions can be symbolic
```

[forgetful_commander.zip](https://github.com/angr/angr/files/2825819/forgetful_commander.zip)


Discussion:

Resolving patch:
diff --git a/angr/analyses/variable_recovery/variable_recovery.py b/angr/analyses/variable_recovery/variable_recovery.py
--- a/angr/analyses/variable_recovery/variable_recovery.py
+++ b/angr/analyses/variable_recovery/variable_recovery.py
@@ -2,6 +2,7 @@
 from collections import defaultdict
 from functools import reduce
 
+import claripy
 import angr # type annotations; pylint: disable=unused-import
 
 from ... import BP, BP_AFTER
@@ -141,6 +142,12 @@ def _merge_concrete_states(self, other):
     def _hook_register_read(self, state):
 
         reg_read_offset = state.inspect.reg_read_offset
+        if isinstance(reg_read_offset, claripy.ast.BV):
+            if reg_read_offset.multivalued:
+                # Multi-valued register offsets are not supported
+                l.warning("Multi-valued register offsets are not supported.")
+                return
+            reg_read_offset = state.solver.eval(reg_read_offset)
         reg_read_length = state.inspect.reg_read_length
 
         if reg_read_offset == state.arch.sp_offset and reg_read_length == state.arch.bytes:
@@ -165,6 +172,12 @@ def _hook_register_read(self, state):
     def _hook_register_write(self, state):
 
         reg_write_offset = state.inspect.reg_write_offset
+        if isinstance(reg_write_offset, claripy.ast.BV):
+            if reg_write_offset.multivalued:
+                # Multi-valued register offsets are not supported
+                l.warning("Multi-valued register offsets are not supported.")
+                return
+            reg_write_offset = state.solver.eval(reg_write_offset)
 
         if reg_write_offset == state.arch.sp_offset:
             # it's updating stack pointer. skip


Test patch: