Repository: angr/angr

Issue:
Angr may explore a false-positive path
<!--
*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.
-->
The problem is that angr may explore a path that is unlikely to be executed.


### **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.
-->
The testing binary `test` is compiled with `-Os` under the current development version of GCC, and I used the latest version of `angr-dev` to execute the binary.


### **To Reproduce.**
<!--
Please include *both a script to reproduce the crash, and attach the binary used, if possible*
-->

`angr-test.py`
```
# -*- coding: utf-8 -*-
import angr
import claripy
import sys

target_binary = sys.argv[1]
sym_input = claripy.BVS("sym_input", 2*8)
p = angr.Project(target_binary, load_options={'auto_load_libs': False})
state = p.factory.entry_state(args=[target_binary, sym_input],add_options={angr.options.ZERO_FILL_UNCONSTRAINED_MEMORY,
                                           angr.options.ZERO_FILL_UNCONSTRAINED_REGISTERS})
sm = p.factory.simulation_manager(state)
#sm.run()
marker = bytes("b718b", "utf-8")
sm.explore(find=lambda s: marker in s.posix.dumps(1))
print(sm)

# constrain the symbolic input to fall within printable range
for byte in sym_input.chop(bits=8):
    state.add_constraints(byte >= 'A'.encode(), byte <= 'Z'.encode())

for i in range(len(sm.found)):
    print("No.", i, " solution:")
    print("output: ", sm.found[i].posix.dumps(1).decode('utf-8'))
    b1 = sm.found[i].solver.eval(sym_input, cast_to=bytes).rstrip(b'\x00')
    print("org b: ", b1)
    print("hex b: ", b1.decode("utf-8", "ignore"))
```

```
$python3 angr-test.py test
<SimulationManager with 1 active, 1 found>
No. 0  solution:
output:   b0b  b2b  b142b  b644b  b718b 
org b:  b'\x8a\xb0'
hex b:  
```

Similar to #3172, I used the markers to differentiate different blocks, and the sequences (e.g., "b0b  b2b  b142b  b644b  b718b") mean various blocks being executed.

I found this problem as angr can not replay the explored path as reported in #3172, and then I try to diagnose this issue what happened here. As a result, I found that angr may explore a false-positive path. The evidence can be found in Lines 100-107 in the source code `test.c` (attached in the following). We can observe that after executing `marker_644()`, it's unlikely (or impossible) to directly execute `marker_718()` as the body of *for-loop* statement starting from line 104 must be executed.

One thing I want to mention is that this behavior only occurs when the binary is compiled in the development version of GCC, while other versions do not have such a problem. The GCC version I used is `gcc version 13.0.0 20220518 (experimental) (GCC) `. Can this issue be in GCC compiler or angr? I guess angr should have good capabilities to successfully handle it?

### **Additional context.**
<!--
Add any other context about the problem here.
-->
Here are the reproduced files, including the binarie `test`, source code(`test.c` and `test-E.c` which is compiled with `gcc -E`), and python script `angr-test.py` to run angr.
[reproducer.zip](https://github.com/angr/angr/files/8787645/reproducer.zip)


Could you please take a look at this issue when you are free?

Thank you so much for your time!
Best regards,
Haoxin




Discussion:

Resolving patch:
diff --git a/angr/engines/vex/claripy/ccall.py b/angr/engines/vex/claripy/ccall.py
--- a/angr/engines/vex/claripy/ccall.py
+++ b/angr/engines/vex/claripy/ccall.py
@@ -338,7 +338,7 @@ def pc_actions_DEC(state, nbits, res, _, cc_ndep, platform=None):
     af = (res ^ arg_l ^ 1)[data[platform]['CondBitOffsets']['G_CC_SHIFT_A']]
     zf = calc_zerobit(res)
     sf = res[nbits-1]
-    of = claripy.If(sf == arg_l[nbits-1], claripy.BVV(0, 1), claripy.BVV(1, 1))
+    of = claripy.If(arg_l == (1 << (nbits - 1)), claripy.BVV(1, 1), claripy.BVV(0, 1))
     return pc_make_rdata(data[platform]['size'], cf, pf, af, zf, sf, of, platform=platform)
 
 def pc_actions_ADC(state, nbits, cc_dep1, cc_dep2, cc_ndep, platform=None):
@@ -448,7 +448,7 @@ def pc_actions_INC(state, nbits, res, _, cc_ndep, platform=None):
     af = (res ^ arg_l ^ 1)[data[platform]['CondBitOffsets']['G_CC_SHIFT_A']]
     zf = calc_zerobit(res)
     sf = res[nbits-1]
-    of = claripy.If(sf == arg_l[nbits-1], claripy.BVV(0, 1), claripy.BVV(1, 1))
+    of = claripy.If(res == (1 << (nbits - 1)), claripy.BVV(1, 1), claripy.BVV(0, 1))
     return pc_make_rdata(data[platform]['size'], cf, pf, af, zf, sf, of, platform=platform)
 
 def pc_actions_SHL(state, nbits, remaining, shifted, cc_ndep, platform=None):


Test patch: