Skip to content

Add an i386 object whose block simplification needs more than thirty passes - #215

Merged
ltfish merged 1 commit into
masterfrom
feature/blocksimp-fixed-point
Aug 29, 2026
Merged

Add an i386 object whose block simplification needs more than thirty passes#215
ltfish merged 1 commit into
masterfrom
feature/blocksimp-fixed-point

Conversation

@zardus

@zardus zardus commented Aug 29, 2026

Copy link
Copy Markdown
Member

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Problem

angr's BlockSimplifier gives up after thirty simplification passes and logs, at ERROR,
Simplification does not reach a fixed point after 30 iterations. Block comparison is probably incorrect. Nothing committed here reaches that cap, so a consumer cannot assert on it. Across
the two largest committed Windows PE objects the deepest any block needed was eight passes.

Root cause

Ordinary compiler output does not produce the shape. When the stack pointer is resolved the
decompiler's propagator folds a whole chain of stack-pointer definitions in one pass, however
long the chain is; a hundred consecutive push instructions on their own still converge in
four passes. The cap is reached only when the frame is hidden from the stack-pointer tracker
and a long chain follows it, which in the wild is packed or partly undecodable code.

Fix

tests/i386/deep_sp_chain, 8952 bytes, gcc output from tests_src/deep_sp_chain.c with the
recipe recorded at the top of that source. caller100 realigns esp through a register,
which hides the frame, and then makes a hundred-argument cdecl call; the resulting block needs
98 passes.

Testing

Decompiling caller100 at 0x8049203 with angr at a9ca247a5 emits that ERROR three times
and still returns 5281 characters of C, which is what makes the message worth acting on. The
consumer is the angr pull request that links this one.

Validation: #215 (comment)

session: sharpen

…passes

The stack-pointer realignment hides the frame from StackPointerTracker, so the
hundred cdecl argument pushes that follow chain through virtual variables and
the decompiler's propagator resolves one link per simplification pass.
@zardus

zardus commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Validation record for head 1b3711ea1132f8febf654b6cd92d6124e9e2acc9 against baseline a87538bc248531d2a1d22434143e8b9752459bac.

  • Artifact: tests/i386/deep_sp_chain, sha256 d349f42010c83ccd067defa5a334c8d04fd9b8af4059330fadf0996c15472667, 8952 bytes
  • Header, read back from the committed bytes: ELF32, little-endian, e_type 2 (ET_EXEC), e_machine 3 (EM_386), statically linked, not stripped
  • Provenance: rebuilt from the recipe recorded at the top of tests_src/deep_sp_chain.c, in a clean directory, twice — identical sha256 both times
  • Local paths: the object carries no /nix/store, no home directory and no user name
  • Consumer at baseline: angr.Project(...), CFGFast(normalize=True), then Decompiler on caller100 at 0x8049203 emits Simplification does not reach a fixed point after 30 iterations. Block comparison is probably incorrect. three times and returns 5281 characters of C
  • Why an inline asm statement is in the source: with the frame resolved, the propagator folds a whole stack-pointer chain in one pass, so a hundred consecutive push instructions converge in four. Nine gcc builds at -O0, -O1 and -O2 with 40, 60 and 100 cdecl arguments, with __builtin_alloca, with an over-aligned local and with force_align_arg_pointer all peaked at four to five passes. The three-instruction realignment is what hides the frame, the way an undecodable instruction does in the packed binaries where this occurs in the wild
  • Rarity: over a corpus decompilation sweep this diagnostic was recorded on one function of one object, both records with status ok. Over the two largest committed Windows PE objects, 35,819 blocks, the deepest simplification any block needed was 8 passes

Caveats: this repository configures no pre-commit hooks and no CI, so nothing beyond the readback above ran here.

session: sharpen

@zardus

zardus commented Aug 29, 2026

Copy link
Copy Markdown
Member Author

THIS MESSAGE WAS GENERATED BY AN AUTOMATED PROCESS

Decompiling caller100 at 0x8049203 in tests/i386/deep_sp_chain, with the consumer at angr
master and with the consumer's change applied. Only the ERROR records differ; the emitted C is
byte-identical on both sides, which is the point the fixture exists to make.

Before — angr reports, three times, that its own output is probably incorrect:

angr master a9ca247a5
function callee100 at 0x8049000, 2 blocks
function caller100 at 0x8049203, 3 blocks
function _start at 0x80492e8, 2 blocks
function sub_8049304 at 0x8049304, 1 blocks
=== decompiling caller100 at 0x8049203
ERROR | angr.analyses.decompiler.block_simplifier | Simplification does not reach a fixed point after 30 iterations. Block comparison is probably incorrect.
ERROR | angr.analyses.decompiler.block_simplifier | Simplification does not reach a fixed point after 30 iterations. Block comparison is probably incorrect.
ERROR | angr.analyses.decompiler.block_simplifier | Simplification does not reach a fixed point after 30 iterations. Block comparison is probably incorrect.
codegen: 5281 chars

After — the loop reaches its fixed point and there is nothing to report:

angr with the simplification bound taken from the block
function callee100 at 0x8049000, 2 blocks
function caller100 at 0x8049203, 3 blocks
function _start at 0x80492e8, 2 blocks
function sub_8049304 at 0x8049304, 1 blocks
=== decompiling caller100 at 0x8049203
codegen: 5281 chars

Dumping the emitted C from both sides and comparing gives 6328 bytes each and no difference.

Both sides ran the same script over the same object; only the angr tree differed, and each side
printed the resolved angr.__file__ to prove which one it had imported.

session: sharpen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants