Repository: angr/angr

Issue:
stdin dependent symbolic execution is broken by gcc 11.2
<!--
*Disclaimer:
The angr suite is maintained by a small team of volunteers.
While we cannot guarantee any timeliness for fixes and enhancements, we will do our best.
For more real-time help with angr, from us and the community, join our [Slack.](https://angr.io/invite/)*
-->
---

**Describe the bug.**

<!--
Please include a clear and concise description of what the bug is.
-->

When working with binaries compiled with gcc 11.2 symbolic execution always takes only one path as if the stdin which they should depend on wasn't symbolic at all.

**Environment Information.**
<!--
Many common issues are caused by problems with the local Python environment.
Before submitting, double-check that your versions of all modules in the angr suite (angr, cle, pyvex, ...) are up to date.
Please include the output of `python -m angr.misc.bug_report` here.
-->
Tested on arch linux and fedora, freshly installed angr environment from pip (angr 9.1.11752)


**To Reproduce.**

<!--
Please include *both a script to reproduce the crash, and attach the binary used, if possible*
-->
Compile this test code with gcc:
```c
#include <stdio.h>
int main() {
    int a;
    scanf("%d", &a);
    if(a == 0)
        printf("do this\n");
    else
        printf("do that\n");
    return 0;
}
```
The angr script:
```py
import angr, claripy

p = angr.Project('test')

entry = p.factory.entry_state()
simgr = p.factory.simgr(entry)

simgr.run()

import IPython
IPython.embed()
```
When complied with gcc 11.1:
```
In [1]: simgr
Out[1]: <SimulationManager with 2 deadended>
```
When compiled with gcc 11.2:
```
In [1]: simgr
Out[1]: <SimulationManager with all stashes empty (1 errored)>
```



Discussion:
When you see the angr output from the gcc 11.2 binary, that always means something has triggered a bug in angr and so the symbolic execution results are not complete. Can you print out `simgr.errored.reraise()`? Can you also upload the 11.2 binary so I don't have to install another gcc myself?
The 11.2 test binary: [here](https://gist.github.com/GbourFox/b92c070d55e324cfed51fec14f121107#file-test)
The trace from reraise:
```
----> 1 simgr.errored[0].reraise()

~/.local/lib/python3.10/site-packages/angr/sim_manager.py in reraise(self)
    816 
    817     def reraise(self):
--> 818         raise self.error.with_traceback(self.traceback)
    819 
    820     def __repr__(self):

~/.local/lib/python3.10/site-packages/angr/sim_manager.py in step_state(self, state, successor_func, **run_args)
    400         """
    401         try:
--> 402             successors = self.successors(state, successor_func=successor_func, **run_args)
    403             stashes = {None: successors.flat_successors,
    404                        'unsat': successors.unsat_successors,

~/.local/lib/python3.10/site-packages/angr/sim_manager.py in successors(self, state, successor_func, **run_args)
    439         if successor_func is not None:
    440             return successor_func(state, **run_args)
--> 441         return self._project.factory.successors(state, **run_args)
    442 
    443     #

~/.local/lib/python3.10/site-packages/angr/factory.py in successors(self, engine, *args, **kwargs)
     58         if engine is not None:
     59             return engine.process(*args, **kwargs)
---> 60         return self.default_engine.process(*args, **kwargs)
     61 
     62     def blank_state(self, **kwargs):

~/.local/lib/python3.10/site-packages/angr/engines/vex/light/slicing.py in process(self, skip_stmts, last_stmt, whitelist, *args, **kwargs)
     17         self._last_stmt = last_stmt
     18         self._whitelist = whitelist
---> 19         return super().process(*args, **kwargs)
     20 
     21     def handle_vex_block(self, irsb):

~/.local/lib/python3.10/site-packages/angr/engines/engine.py in process(***failed resolving arguments***)
    147         self.successors = new_state._inspect_getattr('sim_successors', self.successors)
    148         try:
--> 149             self.process_successors(self.successors, **kwargs)
    150         except SimException:
    151             if o.EXCEPTION_HANDLING not in old_state.options:

~/.local/lib/python3.10/site-packages/angr/engines/failure.py in process_successors(self, successors, **kwargs)
     19             return self.process_procedure(state, successors, terminator, **kwargs)
     20 
---> 21         return super().process_successors(successors, **kwargs)
     22 
     23 from ..errors import AngrExitError

~/.local/lib/python3.10/site-packages/angr/engines/syscall.py in process_successors(self, successors, **kwargs)
     16         # we have at this point entered the next step so we need to check the previous jumpkind
     17         if not state.history or not state.history.parent or not state.history.parent.jumpkind or not state.history.parent.jumpkind.startswith('Ijk_Sys'):
---> 18             return super().process_successors(successors, **kwargs)
     19 
     20         l.debug("Invoking system call handler")

~/.local/lib/python3.10/site-packages/angr/engines/hook.py in process_successors(self, successors, procedure, **kwargs)
     52             procedure = self._lookup_hook(state, procedure)
     53         if procedure is None:
---> 54             return super().process_successors(successors, procedure=procedure, **kwargs)
     55 
     56         if isinstance(procedure.addr, SootAddressDescriptor):

~/.local/lib/python3.10/site-packages/angr/engines/unicorn.py in process_successors(self, successors, **kwargs)
    167         state = self.state
    168         if not self.__check(**kwargs):
--> 169             return super().process_successors(successors, **kwargs)
    170 
    171         extra_stop_points = kwargs.get('extra_stop_points', None)

~/.local/lib/python3.10/site-packages/angr/engines/soot/engine.py in process_successors(self, successors, **kwargs)
     63         state = self.state
     64         if not isinstance(state._ip, SootAddressDescriptor):
---> 65             return super().process_successors(successors, **kwargs)
     66         addr = state._ip
     67 

~/.local/lib/python3.10/site-packages/angr/engines/vex/heavy/heavy.py in process_successors(self, successors, irsb, insn_text, insn_bytes, thumb, size, num_inst, extra_stop_points, opt_level, **kwargs)
     94         while True:
     95             if irsb is None:
---> 96                 irsb = self.lift_vex(
     97                     addr=addr,
     98                     state=self.state,

~/.local/lib/python3.10/site-packages/angr/engines/vex/lifter.py in lift_vex(self, addr, state, clemory, insn_bytes, offset, arch, size, num_inst, traceflags, thumb, extra_stop_points, opt_level, strict_block_end, skip_stmts, collect_data_refs, cross_insn_opt, load_from_ro_regions)
    236                 raise SimEngineError("No bytes in memory for block starting at %#x." % addr)
    237         elif not buff:
--> 238             raise SimEngineError("No bytes in memory for block starting at %#x." % addr)
    239 
    240         # phase 5: call into pyvex

SimEngineError: No bytes in memory for block starting at 0x0.
```

Resolving patch:
diff --git a/angr/procedures/glibc/__libc_start_main.py b/angr/procedures/glibc/__libc_start_main.py
--- a/angr/procedures/glibc/__libc_start_main.py
+++ b/angr/procedures/glibc/__libc_start_main.py
@@ -134,8 +134,11 @@ def run(self, main, argc, argv, init, fini):
 
         # TODO: __cxa_atexit calls for various at-exit needs
 
-        self.call(self.init, (self.argc[31:0], self.argv, self.envp), 'after_init',
-            prototype = 'int main(int argc, char **argv, char **envp)')
+        if not self.state.solver.is_true(self.init == 0):
+            self.call(self.init, (self.argc[31:0], self.argv, self.envp), 'after_init',
+                prototype = 'int main(int argc, char **argv, char **envp)')
+        else:
+            self.after_init(main, argc, argv, init, fini)
 
     def after_init(self, main, argc, argv, init, fini, exit_addr=0):
         self.call(self.main, (self.argc[31:0], self.argv, self.envp), 'after_main',


Test patch: