Skip to content

[Pipeline Review] Reachability treats every predecessorless block as an entry #51

Description

@agorevski

Problem

Reachability, loop detection, and dominance all treat every block with no predecessors as an entry block. This marks unreachable code and metadata-created blocks as reachable.

Evidence

  • src/bytecode_analyzer.py:627-628 starts reachability from every value returned by _entry_blocks.
  • src/bytecode_analyzer.py:637-642 defines entries as all blocks with no predecessors, falling back to the first block only if none exist.
  • Reproduction with bytecode 0x005b00 (STOP; JUMPDEST; STOP) currently reports both blocks reachable:
block_0000 pred=[] succ=[] reachable=True dead=False
block_0001 pred=[] succ=[] reachable=True dead=False

block_0001 cannot be reached from the bytecode entry point.

Why it matters

The analyzer disassembles full runtime bytecode and scans all JUMPDESTs. Solidity metadata or intentionally unreachable blocks can contain JUMPDEST bytes; treating them as entries pollutes reachability, dominance, loop metadata, and fallback/all-block TAC. This creates noisy model inputs and can hide genuinely dead code.

Suggested fix

Use the actual code entry point as the root for contract-level reachability. If function-level reachability is needed, compute it separately from identified dispatcher targets. Consider stripping CBOR metadata before CFG construction so metadata bytes cannot become pseudo-blocks.

Validation/tests to add

  • Unit test for STOP; JUMPDEST; STOP that marks the second block dead.
  • Fixture with Solidity CBOR metadata containing 0x5b bytes to ensure metadata does not become reachable TAC.
  • Regression tests that dominance/loop analysis do not use unreachable predecessorless blocks as roots.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions