Repository: angr/angr

Issue:
Avoiding Infinite Loops with loopseer technique
Hello,

I'm trying to run some code which I know is just a loop running infinitely but I do want to exit this loop (hard coded limit) after it iterated 10 times.
I thought loopseer suppose to do just that but it doesn't seems to stop after 10 iterations but keep going non stop.

My example code:
```
void loop_forever(int a) {
        int i;
        for(i=0;;i++) {
                printf("Test infinite loop");
        }
}
```

Example usage:
```
p = angr.Project(loop_forever, load_options={"auto_load_libs": False})
cfg = p.analyses.CFGFast(normalize = True)
state = p.factory.call_state(addr=0x10438) # 0x10438 = loop_forever function
simgr = p.factory.simulation_manager(state)
simgr.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=10))
while len(simgr.active) != 0:
    simgr.step()
    print simgr.active
```

The binary (elf, 32bit arm) can be download here: https://ufile.io/9nym2


Discussion:
Ok, so I debugged this, and the problem is the argument `stash` is always `None`. Before #888 the exploration technique's `step` method is called after this line in https://github.com/angr/angr/blob/master/angr/sim_manager.py#L319 so everything was fine. @danse-macabre can you take a look at this please? Or just tell me what should be done to fix this.

Thanks
@tyb0807 If I remember correctly, this exact problem was discussed in #934. I've posted a possible workaround there, but there must be a better solution to this problem.
So I guess we should set the default `stash` to `active` instead of `None` now?
Yep, I think so.
I tried to add:
```
# hookset.py
    @staticmethod
    def provide_defaults(**defaults):
        def _func_wrapper(func):
            def _defaults_provider(*args, **kwargs):
                for k, v in defaults.iteritems():
                    if k not in kwargs or kwargs[k] is None:
                        kwargs[k] = v
                return func(*args, **kwargs)
            return _defaults_provider
        return _func_wrapper

# sim_manager.py
    @ImmutabilityMixin.immutable
    @HookSet.provide_defaults(stash='active')
    def step(self, n=None, selector_func=None, step_func=None, stash=None,
             successor_func=None, until=None, filter_func=None, **run_args):
                 # ...
```
to the code as written in the post but it didn't seem to solve the problem.
Is there anything else to change in the code?

Resolving patch:
diff --git a/angr/exploration_techniques/loop_seer.py b/angr/exploration_techniques/loop_seer.py
--- a/angr/exploration_techniques/loop_seer.py
+++ b/angr/exploration_techniques/loop_seer.py
@@ -91,11 +91,9 @@ def setup(self, simgr):
                 entry = loop.entry_edges[0][0]
                 self.loops[entry.addr] = loop
 
-    def step(self, simgr, stash=None, **kwargs):
+    def step(self, simgr, stash='active', **kwargs):
         kwargs['successor_func'] = self.normalized_step
 
-        simgr.step(stash=stash, **kwargs)
-
         for state in simgr.stashes[stash]:
             # Processing a currently running loop
             if state.loop_data.current_loop:
@@ -126,7 +124,7 @@ def step(self, simgr, stash=None, **kwargs):
                     state.loop_data.current_loop.pop()
 
                 if self.bound is not None:
-                    if state.loop_data.trip_counts[header][-1] >= self.bound:
+                    if state.loop_data.trip_counts[header][-1] > self.bound:
                         if self.bound_reached is not None:
                             simgr = self.bound_reached(simgr)
                         else:
@@ -146,6 +144,8 @@ def step(self, simgr, stash=None, **kwargs):
                 state.loop_data.trip_counts[header].append(0)
                 state.loop_data.current_loop.append((loop, exits))
 
+        simgr.step(stash=stash, **kwargs)
+
         return simgr
 
     def normalized_step(self, state):


Test patch:
diff --git a/tests/test_loop_seer.py b/tests/test_loop_seer.py
--- a/tests/test_loop_seer.py
+++ b/tests/test_loop_seer.py
@@ -130,7 +130,7 @@ def test_loop_limiter():
     simgr.run()
 
     nose.tools.assert_true('spinning' in simgr.stashes)
-    nose.tools.assert_equals(simgr.spinning[0].loop_data.trip_counts[0x4005fd][0], 5)
+    nose.tools.assert_equals(simgr.spinning[0].loop_data.trip_counts[0x4005fd][0], 6)
 
 
 if __name__ == "__main__":